Compare commits
148 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
163
CHANGELOG.md
163
CHANGELOG.md
@ -1,3 +1,164 @@
|
||||
<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 wihtout 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>
|
||||
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
|
||||
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
|
||||
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
|
||||
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
|
||||
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
|
||||
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
|
||||
|
||||
|
||||
|
||||
<a name="2.3.0-beta.0"></a>
|
||||
# [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** assert xliff messages have translations ([7908679](https://github.com/angular/angular/commit/7908679)), closes [#12815](https://github.com/angular/angular/issues/12815) [#12604](https://github.com/angular/angular/issues/12604)
|
||||
* **compiler:** updates hash algo for xmb/xtb files ([2f14415](https://github.com/angular/angular/commit/2f14415))
|
||||
* **core:** fix placeholders handling in i18n. ([76e4911](https://github.com/angular/angular/commit/76e4911)), closes [#12512](https://github.com/angular/angular/issues/12512)
|
||||
* **core:** misc i18n fixes ([ed5e98d](https://github.com/angular/angular/commit/ed5e98d))
|
||||
* **core:** xmb serializer uses decimal messaged IDs ([08c038e](https://github.com/angular/angular/commit/08c038e)), closes [#12511](https://github.com/angular/angular/issues/12511)
|
||||
* **platform-browser:** enable AOT ([efbbefd](https://github.com/angular/angular/commit/efbbefd)), closes [#12783](https://github.com/angular/angular/issues/12783)
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add `attachView` / `detachView` to ApplicationRef ([9f7d32a](https://github.com/angular/angular/commit/9f7d32a)), closes [#9293](https://github.com/angular/angular/issues/9293)
|
||||
* **core:** expose `ViewRef` as `ChangeDetectorRef` ([1b5384e](https://github.com/angular/angular/commit/1b5384e)), closes [#12722](https://github.com/angular/angular/issues/12722)
|
||||
* **core:** implements a decimal fingerprint for i18n ([582550a](https://github.com/angular/angular/commit/582550a))
|
||||
* **router:** register router with ngprobe ([c2fae72](https://github.com/angular/angular/commit/c2fae72))
|
||||
* **router_link:** add skipLocationChange and replaceUrl inputs ([#12850](https://github.com/angular/angular/issues/12850)) ([46d1502](https://github.com/angular/angular/commit/46d1502))
|
||||
|
||||
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>
|
||||
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** only pass in same typed players as previous players into web-animations ([#12907](https://github.com/angular/angular/issues/12907)) ([583d283](https://github.com/angular/angular/commit/583d283))
|
||||
* **animations:** retain styling when transition destinations are changed ([#12208](https://github.com/angular/angular/issues/12208)) ([5c46c49](https://github.com/angular/angular/commit/5c46c49)), closes [#9661](https://github.com/angular/angular/issues/9661)
|
||||
* **core:** support `ngTemplateOutlet` in production mode ([#12921](https://github.com/angular/angular/issues/12921)) ([4628798](https://github.com/angular/angular/commit/4628798)), closes [#12911](https://github.com/angular/angular/issues/12911)
|
||||
* **http:** correctly handle response body for 204 status code ([21a4de9](https://github.com/angular/angular/commit/21a4de9)), closes [#12830](https://github.com/angular/angular/issues/12830) [#12393](https://github.com/angular/angular/issues/12393)
|
||||
* **http:** return request url if it cannot be retrieved from response ([845ea23](https://github.com/angular/angular/commit/845ea23)), closes [#12837](https://github.com/angular/angular/issues/12837)
|
||||
* **upgrade:** make AoT ngUpgrade work with the testability API and resumeBootstrap() ([#12910](https://github.com/angular/angular/issues/12910)) ([dc1662a](https://github.com/angular/angular/commit/dc1662a))
|
||||
* **platform-browser:** fix disableDebugTools() ([#12918](https://github.com/angular/angular/issues/12918)) ([7b67bad](https://github.com/angular/angular/commit/7b67bad))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([364642d](https://github.com/angular/angular/commit/364642d))
|
||||
* **router:** removes a peer dependency from router to upgrade ([1dcf1f4](https://github.com/angular/angular/commit/1dcf1f4))
|
||||
* **forms** allow for null values in HTML select options bound with ngValue ([e0ce545](https://github.com/angular/angular/commit/e0ce545)), closes [#10349](https://github.com/angular/angular/issues/10349)
|
||||
* **router:** should not create a route state if navigation is canceled ([#12868](https://github.com/angular/angular/issues/12868)) ([dabaf85](https://github.com/angular/angular/commit/dabaf85)), closes [#12776](https://github.com/angular/angular/issues/12776)
|
||||
* **common:** select should allow for null values in HTML select options bound with ngValue ([e02c180](https://github.com/angular/angular/commit/e02c180)), closes [#12829](https://github.com/angular/angular/issues/12829)
|
||||
* **compiler-cli:** support ctorParams in function closure ([#12876](https://github.com/angular/angular/issues/12876)) ([6cdc3b5](https://github.com/angular/angular/commit/6cdc3b5))
|
||||
|
||||
|
||||
<a name="2.2.0"></a>
|
||||
# [2.2.0 upgrade-firebooster](https://github.com/angular/angular/compare/2.2.0-rc.0...2.2.0) (2016-11-14)
|
||||
|
||||
@ -798,7 +959,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
* core:
|
||||
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
|
||||
created during bootstrap instead by calling `NgModuleRef.destroy`.
|
||||
- `AplicationRef.registerDisposeListener` is deprecated.
|
||||
- `ApplicationRef.registerDisposeListener` is deprecated.
|
||||
Use the `ngOnDestroy` lifecycle hook for providers or
|
||||
`NgModuleRef.onDestroy` 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
|
||||
- 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.
|
||||
|
||||
@ -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
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[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
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[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.
|
||||
|
||||
## 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
|
||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
|
45
build.sh
45
build.sh
@ -17,9 +17,14 @@ PACKAGES=(core
|
||||
upgrade
|
||||
router
|
||||
compiler-cli
|
||||
language-service
|
||||
benchpress)
|
||||
BUILD_ALL=true
|
||||
BUNDLE=true
|
||||
VERSION_PREFIX=$(node -p "require('./package.json').version")
|
||||
VERSION_SUFFIX="-$(git log --oneline -1 | awk '{print $1}')"
|
||||
ROUTER_VERSION_PREFIX=$(node -p "require('./package.json').version.replace(/^2/, '3')")
|
||||
REMOVE_BENCHPRESS=false
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
@ -31,6 +36,10 @@ for ARG in "$@"; do
|
||||
--bundle=*)
|
||||
BUNDLE=( "${ARG#--bundle=}" )
|
||||
;;
|
||||
--publish)
|
||||
VERSION_SUFFIX=""
|
||||
REMOVE_BENCHPRESS=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $ARG."
|
||||
exit 1
|
||||
@ -38,6 +47,10 @@ for ARG in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
VERSION="${VERSION_PREFIX}${VERSION_SUFFIX}"
|
||||
ROUTER_VERSION="${ROUTER_VERSION_PREFIX}${VERSION_SUFFIX}"
|
||||
echo "====== BUILDING: Version ${VERSION} (Router ${ROUTER_VERSION})"
|
||||
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||
@ -106,7 +119,13 @@ do
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
UMD_STATIC_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.min.js
|
||||
UMD_UPGRADE_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.min.js
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||
|
||||
if [[ ${PACKAGE} != router ]]; then
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||
fi
|
||||
if [[ ${PACKAGE} == router ]]; then
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/router-license-banner.txt
|
||||
fi
|
||||
|
||||
rm -rf ${DESTDIR}
|
||||
|
||||
@ -126,6 +145,11 @@ do
|
||||
$TSC -p ${SRCDIR}/tsconfig-testing.json
|
||||
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} ====="
|
||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
@ -157,7 +181,6 @@ do
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
|
||||
|
||||
if [[ -e rollup-testing.config.js ]]; then
|
||||
echo "====== Rollup ${PACKAGE} testing"
|
||||
../../../node_modules/.bin/rollup -c rollup-testing.config.js
|
||||
@ -191,8 +214,24 @@ do
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
|
||||
fi
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
fi
|
||||
|
||||
(
|
||||
echo "====== VERSION: Updating version references"
|
||||
cd ${DESTDIR}
|
||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
|
||||
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
|
||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g\" $""(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .)"
|
||||
perl -p -i -e "s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g" $(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .) < /dev/null 2> /dev/null
|
||||
)
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "====== Building examples: ./modules/@angular/examples/build.sh ====="
|
||||
./modules/@angular/examples/build.sh
|
||||
|
||||
if [[ ${REMOVE_BENCHPRESS} == true ]]; then
|
||||
echo ""
|
||||
echo "==== Removing benchpress from publication"
|
||||
rm -r dist/packages-dist/benchpress
|
||||
fi
|
||||
|
@ -4,7 +4,7 @@ machine:
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
- npm install -g npm@3.6.0
|
||||
|
||||
test:
|
||||
override:
|
||||
|
@ -50,7 +50,9 @@ module.exports = function(config) {
|
||||
'dist/all/@angular/**/e2e_test/**',
|
||||
'dist/all/@angular/router/**',
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/@angular/compiler/test/aot/**',
|
||||
'dist/all/@angular/benchpress/**',
|
||||
'dist/all/@angular/language-service/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
'dist/examples/**/e2e_test/**',
|
||||
|
@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@angular/core": "^2.0.0-rc.7",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"rxjs": "5.0.0-beta.12",
|
||||
"rxjs": "5.0.0-rc.4",
|
||||
"jpm": "1.1.4",
|
||||
"firefox-profile": "0.4.0",
|
||||
"selenium-webdriver": "^2.53.3"
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/* tslint:disable:no-console */
|
||||
import {browser} from 'protractor';
|
||||
|
||||
const assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
|
@ -11,9 +11,6 @@
|
||||
* @description
|
||||
* Entry point for all public APIs of the common package.
|
||||
*/
|
||||
export * from './src/location';
|
||||
export {NgLocalization} from './src/localization';
|
||||
export {CommonModule} from './src/common_module';
|
||||
export * from './src/common';
|
||||
|
||||
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './src/directives/index';
|
||||
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './src/pipes/index';
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
||||
|
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(
|
||||
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';
|
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';
|
@ -96,7 +96,7 @@ export class Location {
|
||||
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
||||
*/
|
||||
prepareExternalUrl(url: string): string {
|
||||
if (url.length > 0 && !url.startsWith('/')) {
|
||||
if (url && url[0] !== '/') {
|
||||
url = '/' + url;
|
||||
}
|
||||
return this._platformStrategy.prepareExternalUrl(url);
|
||||
@ -143,7 +143,7 @@ export class Location {
|
||||
* is.
|
||||
*/
|
||||
public static normalizeQueryParams(params: string): string {
|
||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||
return params && params[0] !== '?' ? '?' + params : params;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,25 +175,13 @@ export class Location {
|
||||
/**
|
||||
* If url has a trailing slash, remove it, otherwise return url as is.
|
||||
*/
|
||||
public static stripTrailingSlash(url: string): string {
|
||||
if (/\/$/g.test(url)) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
public static stripTrailingSlash(url: string): string { return url.replace(/\/$/, ''); }
|
||||
}
|
||||
|
||||
function _stripBaseHref(baseHref: string, url: string): string {
|
||||
if (baseHref.length > 0 && url.startsWith(baseHref)) {
|
||||
return url.substring(baseHref.length);
|
||||
}
|
||||
return url;
|
||||
return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
|
||||
}
|
||||
|
||||
function _stripIndexHtml(url: string): string {
|
||||
if (/\/index.html$/g.test(url)) {
|
||||
// '/index.html'.length == 11
|
||||
return url.substring(0, url.length - 11);
|
||||
}
|
||||
return url;
|
||||
return url.replace(/\/index.html$/, '');
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
|
||||
* {@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
|
||||
* 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'}
|
||||
*
|
||||
|
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 {TestBed} from '@angular/core/testing';
|
||||
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
import {TestBed, inject} from '@angular/core/testing';
|
||||
|
||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('l10n', () => {
|
||||
|
||||
@ -141,6 +138,23 @@ export function main() {
|
||||
expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5');
|
||||
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"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,9 @@
|
||||
* 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 {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
||||
export {CodeGenerator} from './src/codegen';
|
||||
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './src/compiler_host';
|
||||
export {Extractor} from './src/extractor';
|
||||
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
|
||||
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
|
||||
|
||||
export * from '@angular/tsc-wrapped';
|
||||
export {VERSION} from './src/version';
|
||||
|
@ -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 {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 {AnimateCmp} from './animate';
|
||||
@ -19,7 +21,7 @@ import {BasicComp} from './basic';
|
||||
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
||||
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
||||
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 {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
|
||||
|
||||
@ -52,7 +54,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
||||
FormsModule,
|
||||
MdButtonModule,
|
||||
ModuleUsingCustomElements,
|
||||
someLibModuleWithProviders(),
|
||||
SomeLibModule.withProviders(),
|
||||
ThirdpartyModule,
|
||||
],
|
||||
providers: [SomeService],
|
||||
|
@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) {
|
||||
entryComponents: [CompUsingLibModuleDirectiveAndPipe],
|
||||
})
|
||||
export class SomeLibModule {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ngModule: SomeLibModule,
|
||||
providers: [
|
||||
ServiceUsingLibModule,
|
||||
provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
|
||||
]
|
||||
};
|
||||
static withProviders() {
|
||||
return {
|
||||
ngModule: SomeLibModule,
|
||||
providers: [
|
||||
ServiceUsingLibModule, provideValueWithEntryComponents(
|
||||
[{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg>
|
||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
||||
<msg id="3772663375917578720">other-3rdP-component</msg>
|
||||
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="3492007542396725315">Welcome</msg>
|
||||
</messagebundle>
|
||||
`;
|
||||
|
||||
@ -79,5 +79,4 @@ describe('template i18n extraction output', () => {
|
||||
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
|
||||
expect(xlf).toEqual(EXPECTED_XLIFF);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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,7 @@
|
||||
"src/module",
|
||||
"src/bootstrap",
|
||||
"test/all_spec",
|
||||
"test/test_summaries",
|
||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "^0.3.0",
|
||||
"@angular/tsc-wrapped": "0.4.2",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -17,13 +17,12 @@ import {readFileSync} from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {PathMappedReflectorHost} from './path_mapped_reflector_host';
|
||||
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
|
||||
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||
import {Console} from './private_import_core';
|
||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
const GENERATED_META_FILES = /\.json$/;
|
||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
|
||||
const PREAMBLE = `/**
|
||||
@ -38,15 +37,15 @@ const PREAMBLE = `/**
|
||||
export class CodeGenerator {
|
||||
constructor(
|
||||
private options: AngularCompilerOptions, private program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {}
|
||||
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
|
||||
private ngCompilerHost: CompilerHost) {}
|
||||
|
||||
// Write codegen in a directory structure matching the sources.
|
||||
private calculateEmitPath(filePath: string): string {
|
||||
let root = this.options.basePath;
|
||||
for (const eachRootDir of this.options.rootDirs || []) {
|
||||
if (this.options.trace) {
|
||||
console.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) {
|
||||
root = eachRootDir;
|
||||
@ -64,33 +63,31 @@ export class CodeGenerator {
|
||||
return path.join(this.options.genDir, relativePath);
|
||||
}
|
||||
|
||||
codegen(options: {transitiveModules: boolean}): Promise<any> {
|
||||
const staticSymbols =
|
||||
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
||||
|
||||
return this.compiler.compileModules(staticSymbols, options).then(generatedModules => {
|
||||
generatedModules.forEach(generatedModule => {
|
||||
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
|
||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
||||
this.host.writeFile(
|
||||
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
||||
});
|
||||
});
|
||||
codegen(): Promise<any> {
|
||||
return this.compiler
|
||||
.compileAll(this.program.getSourceFiles().map(
|
||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
||||
.then(generatedModules => {
|
||||
generatedModules.forEach(generatedModule => {
|
||||
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
||||
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
|
||||
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
|
||||
PREAMBLE + generatedModule.source;
|
||||
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static create(
|
||||
options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program,
|
||||
compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext,
|
||||
resourceLoader?: compiler.ResourceLoader, reflectorHost?: ReflectorHost): CodeGenerator {
|
||||
resourceLoader = resourceLoader || {
|
||||
get: (s: string) => {
|
||||
if (!compilerHost.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(compilerHost.readFile(s));
|
||||
}
|
||||
};
|
||||
tsCompilerHost: ts.CompilerHost, compilerHostContext?: CompilerHostContext,
|
||||
ngCompilerHost?: CompilerHost): CodeGenerator {
|
||||
if (!ngCompilerHost) {
|
||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
|
||||
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
|
||||
new CompilerHost(program, options, context);
|
||||
}
|
||||
const transFile = cliOptions.i18nFile;
|
||||
const locale = cliOptions.locale;
|
||||
let transContent: string = '';
|
||||
@ -101,84 +98,18 @@ export class CodeGenerator {
|
||||
}
|
||||
transContent = readFileSync(transFile, 'utf8');
|
||||
}
|
||||
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
if (!reflectorHost) {
|
||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||
reflectorHost = usePathMapping ?
|
||||
new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) :
|
||||
new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
||||
}
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser =
|
||||
new compiler.I18NHtmlParser(new compiler.HtmlParser(), transContent, cliOptions.i18nFormat);
|
||||
const config = new compiler.CompilerConfig({
|
||||
genDebugInfo: options.debug === true,
|
||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||
logBindingUpdate: false,
|
||||
useJit: false
|
||||
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
|
||||
debug: options.debug === true,
|
||||
translations: transContent,
|
||||
i18nFormat: cliOptions.i18nFormat,
|
||||
locale: cliOptions.locale,
|
||||
excludeFilePattern: options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES :
|
||||
GENERATED_FILES
|
||||
});
|
||||
const normalizer =
|
||||
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||
const expressionParser = new compiler.Parser(new compiler.Lexer());
|
||||
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
||||
const console = new Console();
|
||||
const tmplParser = new compiler.TemplateParser(
|
||||
expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
||||
const resolver = new compiler.CompileMetadataResolver(
|
||||
new compiler.NgModuleResolver(staticReflector),
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
elementSchemaRegistry, normalizer, staticReflector);
|
||||
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
resolver, tmplParser, new compiler.StyleCompiler(urlResolver),
|
||||
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
||||
new compiler.DirectiveWrapperCompiler(
|
||||
config, expressionParser, elementSchemaRegistry, console),
|
||||
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
||||
cliOptions.locale, cliOptions.i18nFormat,
|
||||
new compiler.AnimationParser(elementSchemaRegistry));
|
||||
|
||||
return new CodeGenerator(
|
||||
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
||||
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
|
||||
}
|
||||
}
|
||||
|
||||
export function extractProgramSymbols(
|
||||
program: ts.Program, staticReflector: StaticReflector, reflectorHost: StaticReflectorHost,
|
||||
options: AngularCompilerOptions): StaticSymbol[] {
|
||||
// Compare with false since the default should be true
|
||||
const skipFileNames =
|
||||
options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
||||
|
||||
const staticSymbols: StaticSymbol[] = [];
|
||||
|
||||
program.getSourceFiles()
|
||||
.filter(sourceFile => !skipFileNames.test(sourceFile.fileName))
|
||||
.forEach(sourceFile => {
|
||||
const absSrcPath = reflectorHost.getCanonicalFileName(sourceFile.fileName);
|
||||
|
||||
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
|
||||
if (!moduleMetadata) {
|
||||
console.log(`WARNING: no metadata found for ${absSrcPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const metadata = moduleMetadata['metadata'];
|
||||
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const symbol of Object.keys(metadata)) {
|
||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
||||
// Ignore symbols that are only included to record error information.
|
||||
continue;
|
||||
}
|
||||
staticSymbols.push(reflectorHost.findDeclaration(absSrcPath, symbol, absSrcPath));
|
||||
}
|
||||
});
|
||||
|
||||
return staticSymbols;
|
||||
export function excludeFilePattern(options: AngularCompilerOptions): RegExp {
|
||||
return options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
||||
}
|
||||
|
275
modules/@angular/compiler-cli/src/compiler_host.ts
Normal file
275
modules/@angular/compiler-cli/src/compiler_host.ts
Normal file
@ -0,0 +1,275 @@
|
||||
/**
|
||||
* @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 {AotCompilerHost, StaticSymbol} from '@angular/compiler';
|
||||
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
const NODE_MODULES = '/node_modules/';
|
||||
const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;
|
||||
|
||||
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
||||
readResource(fileName: string): Promise<string>;
|
||||
assumeFileExists(fileName: string): void;
|
||||
}
|
||||
|
||||
export class CompilerHost implements AotCompilerHost {
|
||||
protected metadataCollector = new MetadataCollector();
|
||||
private isGenDirChildOfRootDir: boolean;
|
||||
protected basePath: string;
|
||||
private genDir: string;
|
||||
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||
|
||||
constructor(
|
||||
protected program: ts.Program, protected options: AngularCompilerOptions,
|
||||
protected context: CompilerHostContext) {
|
||||
// normalize the path so that it never ends with '/'.
|
||||
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
|
||||
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
||||
|
||||
const genPath: string = path.relative(this.basePath, this.genDir);
|
||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||
}
|
||||
|
||||
// We use absolute paths on disk as canonical.
|
||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||
|
||||
moduleNameToFileName(m: string, containingFile: string) {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (m.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
||||
}
|
||||
m = m.replace(EXT, '');
|
||||
const resolved =
|
||||
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
|
||||
.resolvedModule;
|
||||
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* We want a moduleId that will appear in import statements in the generated code.
|
||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
||||
*
|
||||
* The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
|
||||
* `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
|
||||
* existing file.
|
||||
*
|
||||
* | genDir | node_module | rootDir
|
||||
* --------------+----------+-------------+----------
|
||||
* generated | relative | relative | n/a
|
||||
* existing file | n/a | absolute | relative(*)
|
||||
*
|
||||
* NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
|
||||
*/
|
||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||
// 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!
|
||||
if (!this.context.fileExists(importedFile)) {
|
||||
this.context.assumeFileExists(importedFile);
|
||||
}
|
||||
|
||||
containingFile = this.rewriteGenDirPath(containingFile);
|
||||
const containingDir = path.dirname(containingFile);
|
||||
// drop extension
|
||||
importedFile = importedFile.replace(EXT, '');
|
||||
|
||||
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
||||
const importModule = nodeModulesIndex === -1 ?
|
||||
null :
|
||||
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
||||
const isGeneratedFile = IS_GENERATED.test(importedFile);
|
||||
|
||||
if (isGeneratedFile) {
|
||||
// rewrite to genDir path
|
||||
if (importModule) {
|
||||
// it is generated, therefore we do a relative path to the factory
|
||||
return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
|
||||
} else {
|
||||
// assume that import is also in `genDir`
|
||||
importedFile = this.rewriteGenDirPath(importedFile);
|
||||
return this.dotRelative(containingDir, importedFile);
|
||||
}
|
||||
} else {
|
||||
// user code import
|
||||
if (importModule) {
|
||||
return importModule;
|
||||
} else {
|
||||
if (!this.isGenDirChildOfRootDir) {
|
||||
// assume that they are on top of each other.
|
||||
importedFile = importedFile.replace(this.basePath, this.genDir);
|
||||
}
|
||||
return this.dotRelative(containingDir, importedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private dotRelative(from: string, to: string): string {
|
||||
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
||||
return rPath.startsWith('.') ? rPath : './' + rPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
||||
*/
|
||||
private rewriteGenDirPath(filepath: string) {
|
||||
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
||||
if (nodeModulesIndex !== -1) {
|
||||
// If we are in node_modulse, transplant them into `genDir`.
|
||||
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
||||
} else {
|
||||
// pretend that containing file is on top of the `genDir` to normalize the paths.
|
||||
// we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
|
||||
return filepath.replace(this.basePath, this.genDir);
|
||||
}
|
||||
}
|
||||
|
||||
protected getSourceFile(filePath: string): ts.SourceFile {
|
||||
const sf = this.program.getSourceFile(filePath);
|
||||
if (!sf) {
|
||||
if (this.context.fileExists(filePath)) {
|
||||
const sourceText = this.context.readFile(filePath);
|
||||
return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
|
||||
}
|
||||
throw new Error(`Source file ${filePath} not present in program.`);
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
getMetadataFor(filePath: string): ModuleMetadata[] {
|
||||
if (!this.context.fileExists(filePath)) {
|
||||
// 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
|
||||
// exists for the module (i.e. jQuery or angularjs).
|
||||
return;
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
||||
if (this.context.fileExists(metadataPath)) {
|
||||
return this.readMetadata(metadataPath, filePath);
|
||||
}
|
||||
} else {
|
||||
const sf = this.getSourceFile(filePath);
|
||||
const metadata = this.metadataCollector.getMetadata(sf);
|
||||
return metadata ? [metadata] : [];
|
||||
}
|
||||
}
|
||||
|
||||
readMetadata(filePath: string, dtsFilePath: string): ModuleMetadata[] {
|
||||
let metadatas = this.resolverCache.get(filePath);
|
||||
if (metadatas) {
|
||||
return metadatas;
|
||||
}
|
||||
try {
|
||||
const metadataOrMetadatas = JSON.parse(this.context.readFile(filePath));
|
||||
const metadatas = metadataOrMetadatas ?
|
||||
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
||||
[];
|
||||
const v1Metadata = metadatas.find((m: any) => m['version'] === 1);
|
||||
let v2Metadata = metadatas.find((m: any) => m['version'] === 2);
|
||||
if (!v2Metadata && v1Metadata) {
|
||||
// patch up v1 to v2 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
|
||||
// the metadata and the `extends` clause.
|
||||
v2Metadata = {'__symbolic': 'module', 'version': 2, 'metadata': {}};
|
||||
if (v1Metadata.exports) {
|
||||
v2Metadata.exports = v1Metadata.exports;
|
||||
}
|
||||
for (let prop in v1Metadata.metadata) {
|
||||
v2Metadata.metadata[prop] = v1Metadata.metadata[prop];
|
||||
}
|
||||
const sourceText = this.context.readFile(dtsFilePath);
|
||||
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
|
||||
if (exports) {
|
||||
for (let prop in exports.metadata) {
|
||||
if (!v2Metadata.metadata[prop]) {
|
||||
v2Metadata.metadata[prop] = exports.metadata[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
metadatas.push(v2Metadata);
|
||||
}
|
||||
this.resolverCache.set(filePath, metadatas);
|
||||
return metadatas;
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${filePath}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
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 CompilerHostContextAdapter {
|
||||
protected 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 {
|
||||
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 {
|
||||
try {
|
||||
return fs.statSync(directoryName).isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
||||
|
||||
readResource(s: string) {
|
||||
if (!this.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.readFile(s));
|
||||
}
|
||||
}
|
@ -24,17 +24,7 @@ import {Extractor} from './extractor';
|
||||
function extract(
|
||||
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
|
||||
program: ts.Program, host: ts.CompilerHost) {
|
||||
const resourceLoader: compiler.ResourceLoader = {
|
||||
get: (s: string) => {
|
||||
if (!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(host.readFile(s));
|
||||
}
|
||||
};
|
||||
const extractor =
|
||||
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
|
||||
const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host);
|
||||
|
||||
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
|
||||
|
||||
@ -51,9 +41,8 @@ function extract(
|
||||
case 'xliff':
|
||||
case 'xlf':
|
||||
default:
|
||||
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
||||
ext = 'xlf';
|
||||
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
||||
serializer = new compiler.Xliff();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -14,86 +14,30 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
import * as tsc from '@angular/tsc-wrapped';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {extractProgramSymbols} from './codegen';
|
||||
import {ReflectorHost} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
import {StaticReflector, StaticSymbol} from './static_reflector';
|
||||
import {excludeFilePattern} from './codegen';
|
||||
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
|
||||
|
||||
export class Extractor {
|
||||
constructor(
|
||||
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
||||
private metadataResolver: compiler.CompileMetadataResolver) {}
|
||||
private ngExtractor: compiler.Extractor, private ngCompilerHost: CompilerHost,
|
||||
private program: ts.Program) {}
|
||||
|
||||
extract(): Promise<compiler.MessageBundle> {
|
||||
const programSymbols: StaticSymbol[] =
|
||||
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
||||
|
||||
return compiler
|
||||
.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
|
||||
.then(({files}) => {
|
||||
const errors: compiler.ParseError[] = [];
|
||||
|
||||
files.forEach(file => {
|
||||
const compMetas: compiler.CompileDirectiveMetadata[] = [];
|
||||
file.directives.forEach(directiveType => {
|
||||
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
||||
if (dirMeta && dirMeta.isComponent) {
|
||||
compMetas.push(dirMeta);
|
||||
}
|
||||
});
|
||||
compMetas.forEach(compMeta => {
|
||||
const html = compMeta.template.template;
|
||||
const interpolationConfig =
|
||||
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
||||
errors.push(
|
||||
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
|
||||
});
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||
}
|
||||
|
||||
return this.messageBundle;
|
||||
});
|
||||
return this.ngExtractor.extract(this.program.getSourceFiles().map(
|
||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)));
|
||||
}
|
||||
|
||||
static create(
|
||||
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
||||
compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
|
||||
reflectorHost?: ReflectorHost): Extractor {
|
||||
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
||||
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options);
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
|
||||
const config = new compiler.CompilerConfig({
|
||||
genDebugInfo: options.debug === true,
|
||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||
logBindingUpdate: false,
|
||||
useJit: false
|
||||
});
|
||||
|
||||
const normalizer =
|
||||
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
||||
const resolver = new compiler.CompileMetadataResolver(
|
||||
new compiler.NgModuleResolver(staticReflector),
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
elementSchemaRegistry, normalizer, staticReflector);
|
||||
|
||||
// TODO(vicb): implicit tags & attributes
|
||||
const messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
||||
|
||||
return new Extractor(
|
||||
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver);
|
||||
moduleResolverHost: ts.ModuleResolutionHost, ngCompilerHost?: CompilerHost): Extractor {
|
||||
if (!ngCompilerHost)
|
||||
ngCompilerHost =
|
||||
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
|
||||
const {extractor: ngExtractor} = compiler.Extractor.create(
|
||||
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
|
||||
return new Extractor(ngExtractor, ngCompilerHost, program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,19 +19,28 @@ import {CodeGenerator} from './codegen';
|
||||
function codegen(
|
||||
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program,
|
||||
host: ts.CompilerHost) {
|
||||
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen({
|
||||
transitiveModules: true
|
||||
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
|
||||
if (require.main === module) {
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
const project = args.p || args.project || '.';
|
||||
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);
|
||||
});
|
||||
main(args).then((exitCode: number) => process.exit(exitCode));
|
||||
}
|
||||
|
@ -6,29 +6,27 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StaticSymbol} from '@angular/compiler';
|
||||
import {AngularCompilerOptions, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||
import {StaticSymbol} from './static_reflector';
|
||||
import {CompilerHost, CompilerHostContext} from './compiler_host';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
/**
|
||||
* This version of the reflector host expects that the program will be compiled
|
||||
* This version of the AotCompilerHost expects that the program will be compiled
|
||||
* and executed with a "path mapped" directory structure, where generated files
|
||||
* are in a parallel tree with the sources, and imported using a `./` relative
|
||||
* import. This requires using TS `rootDirs` option and also teaching the module
|
||||
* loader what to do.
|
||||
*/
|
||||
export class PathMappedReflectorHost extends ReflectorHost {
|
||||
constructor(
|
||||
program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions,
|
||||
context?: ReflectorHostContext) {
|
||||
super(program, compilerHost, options, context);
|
||||
export class PathMappedCompilerHost extends CompilerHost {
|
||||
constructor(program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext) {
|
||||
super(program, options, context);
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
@ -42,16 +40,23 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
protected resolve(m: string, containingFile: string) {
|
||||
moduleNameToFileName(m: string, containingFile: string) {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (m.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
||||
}
|
||||
for (const root of this.options.rootDirs || ['']) {
|
||||
const rootedContainingFile = path.join(root, containingFile);
|
||||
const resolved =
|
||||
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
||||
if (resolved) {
|
||||
if (this.options.traceResolution) {
|
||||
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||
}
|
||||
return resolved.resolvedFileName;
|
||||
return this.getCanonicalFileName(resolved.resolvedFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,12 +67,9 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
||||
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
|
||||
* they are resolvable by the moduleResolution strategy from the CompilerHost.
|
||||
*/
|
||||
getImportPath(containingFile: string, importedFile: string): string {
|
||||
importedFile = this.resolveAssetUrl(importedFile, containingFile);
|
||||
containingFile = this.resolveAssetUrl(containingFile, '');
|
||||
|
||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||
if (this.options.traceResolution) {
|
||||
console.log(
|
||||
console.error(
|
||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||
}
|
||||
|
||||
@ -82,7 +84,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
||||
}
|
||||
|
||||
const resolvable = (candidate: string) => {
|
||||
const resolved = this.getCanonicalFileName(this.resolve(candidate, importedFile));
|
||||
const resolved = this.moduleNameToFileName(candidate, importedFile);
|
||||
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
|
||||
};
|
||||
|
||||
@ -112,10 +114,10 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
||||
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
||||
}
|
||||
|
||||
getMetadataFor(filePath: string): ModuleMetadata {
|
||||
getMetadataFor(filePath: string): ModuleMetadata[] {
|
||||
for (const root of this.options.rootDirs || []) {
|
||||
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.
|
||||
// This will occur if the user refernced a declared module for which no file
|
||||
// exists for the module (i.e. jQuery or angularjs).
|
||||
@ -124,16 +126,13 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
||||
if (DTS.test(rootedPath)) {
|
||||
const metadataPath = rootedPath.replace(DTS, '.metadata.json');
|
||||
if (this.context.fileExists(metadataPath)) {
|
||||
const metadata = this.readMetadata(metadataPath);
|
||||
return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
|
||||
return this.readMetadata(metadataPath, rootedPath);
|
||||
}
|
||||
} else {
|
||||
const sf = this.program.getSourceFile(rootedPath);
|
||||
if (!sf) {
|
||||
throw new Error(`Source file ${rootedPath} not present in program.`);
|
||||
}
|
||||
sf.fileName = this.getCanonicalFileName(sf.fileName);
|
||||
return this.metadataCollector.getMetadata(sf);
|
||||
const sf = this.getSourceFile(rootedPath);
|
||||
sf.fileName = sf.fileName;
|
||||
const metadata = this.metadataCollector.getMetadata(sf);
|
||||
return metadata ? [metadata] : [];
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,3 @@ export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.Reflectio
|
||||
|
||||
export type Console = typeof r._Console;
|
||||
export var Console: typeof r.Console = r.Console;
|
||||
|
||||
export var reflector: typeof r.reflector = r.reflector;
|
||||
|
||||
export type SetterFn = typeof r._SetterFn;
|
||||
export type GetterFn = typeof r._GetterFn;
|
||||
export type MethodFn = typeof r._MethodFn;
|
||||
|
@ -1,354 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AssetUrl, ImportGenerator} from '@angular/compiler';
|
||||
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
const NODE_MODULES = '/node_modules/';
|
||||
const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;
|
||||
|
||||
export interface ReflectorHostContext {
|
||||
fileExists(fileName: string): boolean;
|
||||
directoryExists(directoryName: string): boolean;
|
||||
readFile(fileName: string): string;
|
||||
assumeFileExists(fileName: string): void;
|
||||
}
|
||||
|
||||
export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
protected metadataCollector = new MetadataCollector();
|
||||
protected context: ReflectorHostContext;
|
||||
private isGenDirChildOfRootDir: boolean;
|
||||
protected basePath: string;
|
||||
private genDir: string;
|
||||
constructor(
|
||||
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
|
||||
protected options: AngularCompilerOptions, context?: ReflectorHostContext) {
|
||||
// normalize the path so that it never ends with '/'.
|
||||
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
|
||||
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
||||
|
||||
this.context = context || new NodeReflectorHostContext(compilerHost);
|
||||
const genPath: string = path.relative(this.basePath, this.genDir);
|
||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||
}
|
||||
|
||||
angularImportLocations() {
|
||||
return {
|
||||
coreDecorators: '@angular/core/src/metadata',
|
||||
diDecorators: '@angular/core/src/di/metadata',
|
||||
diMetadata: '@angular/core/src/di/metadata',
|
||||
diOpaqueToken: '@angular/core/src/di/opaque_token',
|
||||
animationMetadata: '@angular/core/src/animation/metadata',
|
||||
provider: '@angular/core/src/di/provider'
|
||||
};
|
||||
}
|
||||
|
||||
// We use absolute paths on disk as canonical.
|
||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||
|
||||
protected resolve(m: string, containingFile: string) {
|
||||
m = m.replace(EXT, '');
|
||||
const resolved =
|
||||
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
|
||||
.resolvedModule;
|
||||
return resolved ? resolved.resolvedFileName : null;
|
||||
};
|
||||
|
||||
protected normalizeAssetUrl(url: string): string {
|
||||
const assetUrl = AssetUrl.parse(url);
|
||||
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
||||
return this.getCanonicalFileName(path);
|
||||
}
|
||||
|
||||
protected resolveAssetUrl(url: string, containingFile: string): string {
|
||||
const assetUrl = this.normalizeAssetUrl(url);
|
||||
if (assetUrl) {
|
||||
return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want a moduleId that will appear in import statements in the generated code.
|
||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
||||
*
|
||||
* The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
|
||||
* `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
|
||||
* existing file.
|
||||
*
|
||||
* | genDir | node_module | rootDir
|
||||
* --------------+----------+-------------+----------
|
||||
* generated | relative | relative | n/a
|
||||
* existing file | n/a | absolute | relative(*)
|
||||
*
|
||||
* NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
|
||||
*/
|
||||
getImportPath(containingFile: string, importedFile: string): string {
|
||||
importedFile = this.resolveAssetUrl(importedFile, containingFile);
|
||||
containingFile = this.resolveAssetUrl(containingFile, '');
|
||||
|
||||
// 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!
|
||||
if (!this.compilerHost.fileExists(importedFile)) {
|
||||
this.context.assumeFileExists(importedFile);
|
||||
}
|
||||
|
||||
containingFile = this.rewriteGenDirPath(containingFile);
|
||||
const containingDir = path.dirname(containingFile);
|
||||
// drop extension
|
||||
importedFile = importedFile.replace(EXT, '');
|
||||
|
||||
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
||||
const importModule = nodeModulesIndex === -1 ?
|
||||
null :
|
||||
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
||||
const isGeneratedFile = IS_GENERATED.test(importedFile);
|
||||
|
||||
if (isGeneratedFile) {
|
||||
// rewrite to genDir path
|
||||
if (importModule) {
|
||||
// it is generated, therefore we do a relative path to the factory
|
||||
return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
|
||||
} else {
|
||||
// assume that import is also in `genDir`
|
||||
importedFile = this.rewriteGenDirPath(importedFile);
|
||||
return this.dotRelative(containingDir, importedFile);
|
||||
}
|
||||
} else {
|
||||
// user code import
|
||||
if (importModule) {
|
||||
return importModule;
|
||||
} else {
|
||||
if (!this.isGenDirChildOfRootDir) {
|
||||
// assume that they are on top of each other.
|
||||
importedFile = importedFile.replace(this.basePath, this.genDir);
|
||||
}
|
||||
return this.dotRelative(containingDir, importedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private dotRelative(from: string, to: string): string {
|
||||
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
||||
return rPath.startsWith('.') ? rPath : './' + rPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
||||
*/
|
||||
private rewriteGenDirPath(filepath: string) {
|
||||
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
||||
if (nodeModulesIndex !== -1) {
|
||||
// If we are in node_modulse, transplant them into `genDir`.
|
||||
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
||||
} else {
|
||||
// pretend that containing file is on top of the `genDir` to normalize the paths.
|
||||
// we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
|
||||
return filepath.replace(this.basePath, this.genDir);
|
||||
}
|
||||
}
|
||||
|
||||
findDeclaration(
|
||||
module: string, symbolName: string, containingFile: string,
|
||||
containingModule?: string): StaticSymbol {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (module.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = path.join(this.basePath, 'index.ts');
|
||||
}
|
||||
|
||||
try {
|
||||
const assetUrl = this.normalizeAssetUrl(module);
|
||||
if (assetUrl) {
|
||||
module = assetUrl;
|
||||
}
|
||||
const filePath = this.resolve(module, containingFile);
|
||||
|
||||
if (!filePath) {
|
||||
// If the file cannot be found the module is probably referencing a declared module
|
||||
// for which there is no disambiguating file and we also don't need to track
|
||||
// re-exports. Just use the module name.
|
||||
return this.getStaticSymbol(module, symbolName);
|
||||
}
|
||||
|
||||
const tc = this.program.getTypeChecker();
|
||||
const sf = this.program.getSourceFile(filePath);
|
||||
if (!sf || !(<any>sf).symbol) {
|
||||
// The source file was not needed in the compile but we do need the values from
|
||||
// the corresponding .ts files stored in the .metadata.json file. Check the file
|
||||
// for exports to see if the file is exported.
|
||||
return this.resolveExportedSymbol(filePath, symbolName) ||
|
||||
this.getStaticSymbol(filePath, symbolName);
|
||||
}
|
||||
|
||||
let symbol = tc.getExportsOfModule((<any>sf).symbol).find(m => m.name === symbolName);
|
||||
if (!symbol) {
|
||||
throw new Error(`can't find symbol ${symbolName} exported from module ${filePath}`);
|
||||
}
|
||||
if (symbol &&
|
||||
symbol.flags & ts.SymbolFlags.Alias) { // This is an alias, follow what it aliases
|
||||
symbol = tc.getAliasedSymbol(symbol);
|
||||
}
|
||||
const declaration = symbol.getDeclarations()[0];
|
||||
const declarationFile = this.getCanonicalFileName(declaration.getSourceFile().fileName);
|
||||
|
||||
return this.getStaticSymbol(declarationFile, symbol.getName());
|
||||
} catch (e) {
|
||||
console.error(`can't resolve module ${module} from ${containingFile}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private typeCache = new Map<string, StaticSymbol>();
|
||||
private resolverCache = new Map<string, ModuleMetadata>();
|
||||
|
||||
/**
|
||||
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
||||
* All types passed to the StaticResolver should be pseudo-types returned by this method.
|
||||
*
|
||||
* @param declarationFile the absolute path of the file where the symbol is declared
|
||||
* @param name the name of the type.
|
||||
*/
|
||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||
const memberSuffix = members ? `.${ members.join('.')}` : '';
|
||||
const key = `"${declarationFile}".${name}${memberSuffix}`;
|
||||
let result = this.typeCache.get(key);
|
||||
if (!result) {
|
||||
result = new StaticSymbol(declarationFile, name, members);
|
||||
this.typeCache.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getMetadataFor(filePath: string): ModuleMetadata {
|
||||
if (!this.context.fileExists(filePath)) {
|
||||
// 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
|
||||
// exists for the module (i.e. jQuery or angularjs).
|
||||
return;
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
||||
if (this.context.fileExists(metadataPath)) {
|
||||
const metadata = this.readMetadata(metadataPath);
|
||||
return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
|
||||
}
|
||||
} else {
|
||||
const sf = this.program.getSourceFile(filePath);
|
||||
if (!sf) {
|
||||
if (this.context.fileExists(filePath)) {
|
||||
const sourceText = this.context.readFile(filePath);
|
||||
return this.metadataCollector.getMetadata(
|
||||
ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true));
|
||||
}
|
||||
|
||||
throw new Error(`Source file ${filePath} not present in program.`);
|
||||
}
|
||||
return this.metadataCollector.getMetadata(sf);
|
||||
}
|
||||
}
|
||||
|
||||
readMetadata(filePath: string) {
|
||||
try {
|
||||
return this.resolverCache.get(filePath) || JSON.parse(this.context.readFile(filePath));
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${filePath}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private getResolverMetadata(filePath: string): ModuleMetadata {
|
||||
let metadata = this.resolverCache.get(filePath);
|
||||
if (!metadata) {
|
||||
metadata = this.getMetadataFor(filePath);
|
||||
this.resolverCache.set(filePath, metadata);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
protected resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
||||
const resolveModule = (moduleName: string): string => {
|
||||
const resolvedModulePath = this.getCanonicalFileName(this.resolve(moduleName, filePath));
|
||||
if (!resolvedModulePath) {
|
||||
throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`);
|
||||
}
|
||||
return resolvedModulePath;
|
||||
};
|
||||
const metadata = this.getResolverMetadata(filePath);
|
||||
if (metadata) {
|
||||
// If we have metadata for the symbol, this is the original exporting location.
|
||||
if (metadata.metadata[symbolName]) {
|
||||
return this.getStaticSymbol(filePath, symbolName);
|
||||
}
|
||||
|
||||
// If no, try to find the symbol in one of the re-export location
|
||||
if (metadata.exports) {
|
||||
// Try and find the symbol in the list of explicitly re-exported symbols.
|
||||
for (const moduleExport of metadata.exports) {
|
||||
if (moduleExport.export) {
|
||||
const exportSymbol = moduleExport.export.find(symbol => {
|
||||
if (typeof symbol === 'string') {
|
||||
return symbol == symbolName;
|
||||
} else {
|
||||
return symbol.as == symbolName;
|
||||
}
|
||||
});
|
||||
if (exportSymbol) {
|
||||
let symName = symbolName;
|
||||
if (typeof exportSymbol !== 'string') {
|
||||
symName = exportSymbol.name;
|
||||
}
|
||||
return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find the symbol via export * directives.
|
||||
for (const moduleExport of metadata.exports) {
|
||||
if (!moduleExport.export) {
|
||||
const resolvedModule = resolveModule(moduleExport.from);
|
||||
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
||||
if (candidateSymbol) return candidateSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeReflectorHostContext implements ReflectorHostContext {
|
||||
constructor(private host: ts.CompilerHost) {}
|
||||
|
||||
private assumedExists: {[fileName: string]: boolean} = {};
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.assumedExists[fileName] || this.host.fileExists(fileName);
|
||||
}
|
||||
|
||||
directoryExists(directoryName: string): boolean {
|
||||
try {
|
||||
return fs.statSync(directoryName).isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
||||
|
||||
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
|
||||
}
|
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');
|
227
modules/@angular/compiler-cli/test/aot_host_spec.ts
Normal file
227
modules/@angular/compiler-cli/test/aot_host_spec.ts
Normal file
@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost} from '../src/compiler_host';
|
||||
|
||||
import {Directory, Entry, MockAotContext, MockCompilerHost} from './mocks';
|
||||
|
||||
describe('CompilerHost', () => {
|
||||
let context: MockAotContext;
|
||||
let program: ts.Program;
|
||||
let hostNestedGenDir: CompilerHost;
|
||||
let hostSiblingGenDir: CompilerHost;
|
||||
|
||||
beforeEach(() => {
|
||||
context = new MockAotContext('/tmp/src', clone(FILES));
|
||||
const host = new MockCompilerHost(context);
|
||||
program = ts.createProgram(
|
||||
['main.ts'], {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
},
|
||||
host);
|
||||
// Force a typecheck
|
||||
const errors = program.getSemanticDiagnostics();
|
||||
if (errors && errors.length) {
|
||||
throw new Error('Expected no errors');
|
||||
}
|
||||
hostNestedGenDir = new CompilerHost(
|
||||
program, {
|
||||
genDir: '/tmp/project/src/gen/',
|
||||
basePath: '/tmp/project/src',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
hostSiblingGenDir = new CompilerHost(
|
||||
program, {
|
||||
genDir: '/tmp/project/gen',
|
||||
basePath: '/tmp/project/src/',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
});
|
||||
|
||||
describe('nestedGenDir', () => {
|
||||
it('should import node_module from factory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/node_modules/@angular/core.d.ts',
|
||||
'/tmp/project/src/gen/my.ngfactory.ts', ))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should import factory from factory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./my.other.ngfactory');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.css.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../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');
|
||||
});
|
||||
|
||||
it('should import application from factory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('../my.other');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../../my.other');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('../a/my.other');
|
||||
});
|
||||
});
|
||||
|
||||
describe('siblingGenDir', () => {
|
||||
it('should import node_module from factory', () => {
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/node_modules/@angular/core.d.ts',
|
||||
'/tmp/project/src/gen/my.ngfactory.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should import factory from factory', () => {
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./my.other.ngfactory');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.css.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../my.other.css');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./a/my.other.css.shim');
|
||||
});
|
||||
|
||||
it('should import application from factory', () => {
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./my.other');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../my.other');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./a/my.other');
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main @angular/core', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/node_modules/@angular/core.d.ts', '/tmp/project/src/main.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main to a sub-directory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib/utils.ts', 'main.ts')).toEqual('./lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a peer file', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib/collections.ts', 'lib/utils.ts'))
|
||||
.toEqual('./collections');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a sibling directory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib/utils.ts', 'lib2/utils2.ts'))
|
||||
.toEqual('../lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to read a metadata file', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')).toEqual([
|
||||
{__symbolic: 'module', version: 2, metadata: {foo: {__symbolic: 'class'}}}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should be able to read empty metadata ', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return undefined for missing modules', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should add missing v2 metadata from v1 metadata and .d.ts files', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1.d.ts')).toEqual([
|
||||
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||
__symbolic: 'module',
|
||||
version: 2,
|
||||
metadata: {
|
||||
foo: {__symbolic: 'class'},
|
||||
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
|
||||
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}}
|
||||
}
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
const dummyModule = 'export let foo: any[];';
|
||||
|
||||
const FILES: Entry = {
|
||||
'tmp': {
|
||||
'src': {
|
||||
'main.ts': `
|
||||
import * as c from '@angular/core';
|
||||
import * as r from '@angular/router';
|
||||
import * as u from './lib/utils';
|
||||
import * as cs from './lib/collections';
|
||||
import * as u2 from './lib2/utils2';
|
||||
`,
|
||||
'lib': {
|
||||
'utils.ts': dummyModule,
|
||||
'collections.ts': dummyModule,
|
||||
},
|
||||
'lib2': {'utils2.ts': dummyModule},
|
||||
'node_modules': {
|
||||
'@angular': {
|
||||
'core.d.ts': dummyModule,
|
||||
'core.metadata.json':
|
||||
`{"__symbolic":"module", "version": 2, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
|
||||
'unused.d.ts': dummyModule,
|
||||
'empty.d.ts': 'export declare var a: string;',
|
||||
'empty.metadata.json': '[]',
|
||||
}
|
||||
},
|
||||
'metadata_versions': {
|
||||
'v1.d.ts': `
|
||||
export declare class Bar {
|
||||
ngOnInit() {}
|
||||
}
|
||||
export declare class BarChild extends Bar {}
|
||||
`,
|
||||
'v1.metadata.json':
|
||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function clone(entry: Entry): Entry {
|
||||
if (typeof entry === 'string') {
|
||||
return entry;
|
||||
} else {
|
||||
const result: Directory = {};
|
||||
for (const name in entry) {
|
||||
result[name] = clone(entry[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
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));
|
||||
});
|
||||
});
|
@ -6,14 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ReflectorHostContext} from '@angular/compiler-cli/src/reflector_host';
|
||||
import {CompilerHostContext} from '@angular/compiler-cli/src/compiler_host';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export type Entry = string | Directory;
|
||||
|
||||
export interface Directory { [name: string]: Entry; }
|
||||
|
||||
export class MockContext implements ReflectorHostContext {
|
||||
export class MockAotContext implements CompilerHostContext {
|
||||
constructor(public currentDirectory: string, private files: Entry) {}
|
||||
|
||||
fileExists(fileName: string): boolean { return typeof this.getEntry(fileName) === 'string'; }
|
||||
@ -28,6 +28,14 @@ export class MockContext implements ReflectorHostContext {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
readResource(fileName: string): Promise<string> {
|
||||
const result = this.readFile(fileName);
|
||||
if (result == null) {
|
||||
return Promise.reject(new Error(`Resource not found: ${fileName}`));
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
writeFile(fileName: string, data: string): void {
|
||||
const parts = fileName.split('/');
|
||||
const name = parts.pop();
|
||||
@ -89,7 +97,7 @@ function normalize(parts: string[]): string[] {
|
||||
}
|
||||
|
||||
export class MockCompilerHost implements ts.CompilerHost {
|
||||
constructor(private context: MockContext) {}
|
||||
constructor(private context: MockAotContext) {}
|
||||
|
||||
fileExists(fileName: string): boolean { return this.context.fileExists(fileName); }
|
||||
|
||||
|
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;
|
@ -1,329 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReflectorHost} from '../src/reflector_host';
|
||||
|
||||
import {Directory, Entry, MockCompilerHost, MockContext} from './mocks';
|
||||
|
||||
describe('reflector_host', () => {
|
||||
let context: MockContext;
|
||||
let host: ts.CompilerHost;
|
||||
let program: ts.Program;
|
||||
let reflectorNestedGenDir: ReflectorHost;
|
||||
let reflectorSiblingGenDir: ReflectorHost;
|
||||
|
||||
beforeEach(() => {
|
||||
context = new MockContext('/tmp/src', clone(FILES));
|
||||
host = new MockCompilerHost(context);
|
||||
program = ts.createProgram(
|
||||
['main.ts'], {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
},
|
||||
host);
|
||||
// Force a typecheck
|
||||
const errors = program.getSemanticDiagnostics();
|
||||
if (errors && errors.length) {
|
||||
throw new Error('Expected no errors');
|
||||
}
|
||||
reflectorNestedGenDir = new ReflectorHost(
|
||||
program, host, {
|
||||
genDir: '/tmp/project/src/gen/',
|
||||
basePath: '/tmp/project/src',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
reflectorSiblingGenDir = new ReflectorHost(
|
||||
program, host, {
|
||||
genDir: '/tmp/project/gen',
|
||||
basePath: '/tmp/project/src/',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
});
|
||||
|
||||
describe('nestedGenDir', () => {
|
||||
it('should import node_module from factory', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/gen/my.ngfactory.ts',
|
||||
'/tmp/project/node_modules/@angular/core.d.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should import factory from factory', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts'))
|
||||
.toEqual('./my.other.ngfactory');
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts'))
|
||||
.toEqual('../my.other.css');
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts'))
|
||||
.toEqual('./a/my.other.css.shim');
|
||||
});
|
||||
|
||||
it('should import application from factory', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
|
||||
.toEqual('../my.other');
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
|
||||
.toEqual('../../my.other');
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts'))
|
||||
.toEqual('../a/my.other');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nestedGenDir', () => {
|
||||
it('should import node_module from factory', () => {
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/gen/my.ngfactory.ts',
|
||||
'/tmp/project/node_modules/@angular/core.d.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should import factory from factory', () => {
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts'))
|
||||
.toEqual('./my.other.ngfactory');
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts'))
|
||||
.toEqual('../my.other.css');
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts'))
|
||||
.toEqual('./a/my.other.css.shim');
|
||||
});
|
||||
|
||||
it('should import application from factory', () => {
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
|
||||
.toEqual('./my.other');
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
|
||||
.toEqual('../my.other');
|
||||
expect(reflectorSiblingGenDir.getImportPath(
|
||||
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts'))
|
||||
.toEqual('./a/my.other');
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide the import locations for angular', () => {
|
||||
const {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} =
|
||||
reflectorNestedGenDir.angularImportLocations();
|
||||
expect(coreDecorators).toEqual('@angular/core/src/metadata');
|
||||
expect(diDecorators).toEqual('@angular/core/src/di/metadata');
|
||||
expect(diMetadata).toEqual('@angular/core/src/di/metadata');
|
||||
expect(animationMetadata).toEqual('@angular/core/src/animation/metadata');
|
||||
expect(provider).toEqual('@angular/core/src/di/provider');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main @angular/core', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath(
|
||||
'/tmp/project/src/main.ts', '/tmp/project/node_modules/@angular/core.d.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main to a sub-directory', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath('main.ts', 'lib/utils.ts')).toEqual('./lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a peer file', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath('lib/utils.ts', 'lib/collections.ts'))
|
||||
.toEqual('./collections');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a sibling directory', () => {
|
||||
expect(reflectorNestedGenDir.getImportPath('lib2/utils2.ts', 'lib/utils.ts'))
|
||||
.toEqual('../lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to produce a symbol for an exported symbol', () => {
|
||||
expect(reflectorNestedGenDir.findDeclaration('@angular/router', 'foo', 'main.ts'))
|
||||
.toBeDefined();
|
||||
});
|
||||
|
||||
it('should be able to produce a symbol for values space only reference', () => {
|
||||
expect(reflectorNestedGenDir.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts'))
|
||||
.toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
it('should be produce the same symbol if asked twice', () => {
|
||||
const foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
||||
const foo2 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
||||
expect(foo1).toBe(foo2);
|
||||
});
|
||||
|
||||
it('should be able to produce a symbol for a module with no file', () => {
|
||||
expect(reflectorNestedGenDir.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should be able to read a metadata file', () => {
|
||||
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts'))
|
||||
.toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}});
|
||||
});
|
||||
|
||||
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
|
||||
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts'))
|
||||
.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should be able to read empty metadata ', () => {
|
||||
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts'))
|
||||
.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined for missing modules', () => {
|
||||
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts'))
|
||||
.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should be able to trace a named export', () => {
|
||||
const symbol = reflectorNestedGenDir.findDeclaration(
|
||||
'./reexport/reexport.d.ts', 'One', '/tmp/src/main.ts');
|
||||
expect(symbol.name).toEqual('One');
|
||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
||||
});
|
||||
|
||||
it('should be able to trace a renamed export', () => {
|
||||
const symbol = reflectorNestedGenDir.findDeclaration(
|
||||
'./reexport/reexport.d.ts', 'Four', '/tmp/src/main.ts');
|
||||
expect(symbol.name).toEqual('Three');
|
||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
|
||||
});
|
||||
|
||||
it('should be able to trace an export * export', () => {
|
||||
const symbol = reflectorNestedGenDir.findDeclaration(
|
||||
'./reexport/reexport.d.ts', 'Five', '/tmp/src/main.ts');
|
||||
expect(symbol.name).toEqual('Five');
|
||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
|
||||
});
|
||||
|
||||
it('should be able to trace a multi-level re-export', () => {
|
||||
const symbol = reflectorNestedGenDir.findDeclaration(
|
||||
'./reexport/reexport.d.ts', 'Thirty', '/tmp/src/main.ts');
|
||||
expect(symbol.name).toEqual('Thirty');
|
||||
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
|
||||
});
|
||||
});
|
||||
|
||||
const dummyModule = 'export let foo: any[];';
|
||||
|
||||
const FILES: Entry = {
|
||||
'tmp': {
|
||||
'src': {
|
||||
'main.ts': `
|
||||
import * as c from '@angular/core';
|
||||
import * as r from '@angular/router';
|
||||
import * as u from './lib/utils';
|
||||
import * as cs from './lib/collections';
|
||||
import * as u2 from './lib2/utils2';
|
||||
`,
|
||||
'lib': {
|
||||
'utils.ts': dummyModule,
|
||||
'collections.ts': dummyModule,
|
||||
},
|
||||
'lib2': {'utils2.ts': dummyModule},
|
||||
'reexport': {
|
||||
'reexport.d.ts': `
|
||||
import * as c from '@angular/core';
|
||||
`,
|
||||
'reexport.metadata.json': JSON.stringify({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {},
|
||||
exports: [
|
||||
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
|
||||
{from: './src/origin5'}, {from: './src/reexport2'}
|
||||
]
|
||||
}),
|
||||
'src': {
|
||||
'origin1.d.ts': `
|
||||
export class One {}
|
||||
export class Two {}
|
||||
export class Three {}
|
||||
`,
|
||||
'origin1.metadata.json': JSON.stringify({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
One: {__symbolic: 'class'},
|
||||
Two: {__symbolic: 'class'},
|
||||
Three: {__symbolic: 'class'},
|
||||
},
|
||||
}),
|
||||
'origin5.d.ts': `
|
||||
export class Five {}
|
||||
`,
|
||||
'origin5.metadata.json': JSON.stringify({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
Five: {__symbolic: 'class'},
|
||||
},
|
||||
}),
|
||||
'origin30.d.ts': `
|
||||
export class Thirty {}
|
||||
`,
|
||||
'origin30.metadata.json': JSON.stringify({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
Thirty: {__symbolic: 'class'},
|
||||
},
|
||||
}),
|
||||
'originNone.d.ts': dummyModule,
|
||||
'originNone.metadata.json': JSON.stringify({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {},
|
||||
}),
|
||||
'reexport2.d.ts': dummyModule,
|
||||
'reexport2.metadata.json': JSON.stringify({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {},
|
||||
exports: [{from: './originNone'}, {from: './origin30'}]
|
||||
})
|
||||
}
|
||||
},
|
||||
'node_modules': {
|
||||
'@angular': {
|
||||
'core.d.ts': dummyModule,
|
||||
'core.metadata.json':
|
||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
|
||||
'unused.d.ts': dummyModule,
|
||||
'empty.d.ts': 'export declare var a: string;',
|
||||
'empty.metadata.json': '[]',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function clone(entry: Entry): Entry {
|
||||
if (typeof entry === 'string') {
|
||||
return entry;
|
||||
} else {
|
||||
const result: Directory = {};
|
||||
for (const name in entry) {
|
||||
result[name] = clone(entry[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
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": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"module": "commonjs",
|
||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||
@ -14,11 +14,11 @@
|
||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||
},
|
||||
"experimentalDecorators": true,
|
||||
"rootDir": ".",
|
||||
"sourceRoot": ".",
|
||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["integrationtest"],
|
||||
|
@ -21,15 +21,22 @@
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
export {VERSION} from './src/version';
|
||||
export * from './src/template_parser/template_ast';
|
||||
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
|
||||
export {CompilerConfig, RenderTypes} from './src/config';
|
||||
export * from './src/compile_metadata';
|
||||
export * from './src/offline_compiler';
|
||||
export {RuntimeCompiler} from './src/runtime_compiler';
|
||||
export * from './src/aot/compiler_factory';
|
||||
export * from './src/aot/compiler';
|
||||
export * from './src/aot/compiler_host';
|
||||
export * from './src/aot/static_reflector';
|
||||
export * from './src/aot/static_reflection_capabilities';
|
||||
export * from './src/aot/static_symbol';
|
||||
export * from './src/aot/summary_resolver';
|
||||
export {JitCompiler} from './src/jit/compiler';
|
||||
export * from './src/jit/compiler_factory';
|
||||
export * from './src/url_resolver';
|
||||
export * from './src/resource_loader';
|
||||
export * from './src/compiler';
|
||||
export {DirectiveResolver} from './src/directive_resolver';
|
||||
export {PipeResolver} from './src/pipe_resolver';
|
||||
export {NgModuleResolver} from './src/ng_module_resolver';
|
||||
@ -53,5 +60,4 @@ export * from './src/style_compiler';
|
||||
export * from './src/template_parser/template_parser';
|
||||
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
||||
export {AnimationParser} from './src/animation/animation_parser';
|
||||
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
||||
|
||||
@ -41,7 +41,9 @@ const _ANIMATION_TIME_VAR = o.variable('totalTime');
|
||||
const _ANIMATION_START_STATE_STYLES_VAR = o.variable('startStateStyles');
|
||||
const _ANIMATION_END_STATE_STYLES_VAR = o.variable('endStateStyles');
|
||||
const _ANIMATION_COLLECTED_STYLES = o.variable('collectedStyles');
|
||||
const EMPTY_MAP = o.literalMap([]);
|
||||
const _PREVIOUS_ANIMATION_PLAYERS = o.variable('previousPlayers');
|
||||
const _EMPTY_MAP = o.literalMap([]);
|
||||
const _EMPTY_ARRAY = o.literalArr([]);
|
||||
|
||||
class _AnimationBuilder implements AnimationAstVisitor {
|
||||
private _fnVarName: string;
|
||||
@ -67,8 +69,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
stylesArr.push(o.literalMap(entries));
|
||||
});
|
||||
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||
o.importExpr(resolveIdentifier(Identifiers.collectAndResolveStyles)).callFn([
|
||||
return o.importExpr(createIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.collectAndResolveStyles)).callFn([
|
||||
_ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr)
|
||||
])
|
||||
]);
|
||||
@ -76,7 +78,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
|
||||
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext):
|
||||
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)
|
||||
]);
|
||||
}
|
||||
@ -98,7 +100,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
||||
const keyframesExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
||||
o.importExpr(createIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
||||
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
||||
o.literalArr(keyframeExpressions)
|
||||
]);
|
||||
@ -110,24 +112,29 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
_callAnimateMethod(
|
||||
ast: AnimationStepAst, startingStylesExpr: any, keyframesExpr: any,
|
||||
context: _AnimationBuilderContext) {
|
||||
let previousStylesValue: o.Expression = _EMPTY_ARRAY;
|
||||
if (context.isExpectingFirstAnimateStep) {
|
||||
previousStylesValue = _PREVIOUS_ANIMATION_PLAYERS;
|
||||
context.isExpectingFirstAnimateStep = false;
|
||||
}
|
||||
context.totalTransitionTime += ast.duration + ast.delay;
|
||||
return _ANIMATION_FACTORY_RENDERER_VAR.callMethod('animate', [
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, startingStylesExpr, keyframesExpr, o.literal(ast.duration),
|
||||
o.literal(ast.delay), o.literal(ast.easing)
|
||||
o.literal(ast.delay), o.literal(ast.easing), previousStylesValue
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
|
||||
o.Expression {
|
||||
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)
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
||||
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)
|
||||
]);
|
||||
}
|
||||
@ -150,6 +157,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
|
||||
context.totalTransitionTime = 0;
|
||||
context.isExpectingFirstStyleStep = true;
|
||||
context.isExpectingFirstAnimateStep = true;
|
||||
|
||||
const stateChangePreconditions: o.Expression[] = [];
|
||||
|
||||
@ -187,17 +195,16 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
context.stateMap.registerState(DEFAULT_STATE, {});
|
||||
|
||||
const statements: o.Statement[] = [];
|
||||
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
|
||||
.callMethod(
|
||||
'cancelActiveAnimation',
|
||||
statements.push(_PREVIOUS_ANIMATION_PLAYERS
|
||||
.set(_ANIMATION_FACTORY_VIEW_CONTEXT.callMethod(
|
||||
'getAnimationPlayers',
|
||||
[
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
||||
_ANIMATION_NEXT_STATE_VAR.equals(o.literal(EMPTY_STATE))
|
||||
])
|
||||
.toStmt());
|
||||
]))
|
||||
.toDeclStmt());
|
||||
|
||||
|
||||
statements.push(_ANIMATION_COLLECTED_STYLES.set(EMPTY_MAP).toDeclStmt());
|
||||
statements.push(_ANIMATION_COLLECTED_STYLES.set(_EMPTY_MAP).toDeclStmt());
|
||||
statements.push(_ANIMATION_PLAYER_VAR.set(o.NULL_EXPR).toDeclStmt());
|
||||
statements.push(_ANIMATION_TIME_VAR.set(o.literal(0)).toDeclStmt());
|
||||
|
||||
@ -221,18 +228,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR),
|
||||
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
|
||||
|
||||
const RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles));
|
||||
|
||||
// before we start any animation we want to clear out the starting
|
||||
// styles from the element's style property (since they were placed
|
||||
// there at the end of the last animation
|
||||
statements.push(RENDER_STYLES_FN
|
||||
.callFn([
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(resolveIdentifier(Identifiers.clearStyles))
|
||||
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
||||
])
|
||||
.toStmt());
|
||||
const RENDER_STYLES_FN = o.importExpr(createIdentifier(Identifiers.renderStyles));
|
||||
|
||||
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
||||
|
||||
@ -241,7 +237,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
statements.push(new o.IfStmt(
|
||||
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR),
|
||||
[_ANIMATION_PLAYER_VAR
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
|
||||
.toStmt()]));
|
||||
|
||||
// once complete we want to apply the styles on the element
|
||||
@ -259,16 +255,33 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
.callFn([
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(
|
||||
resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||
.callFn([
|
||||
_ANIMATION_START_STATE_STYLES_VAR,
|
||||
_ANIMATION_END_STATE_STYLES_VAR
|
||||
])
|
||||
createIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||
.callFn(
|
||||
[
|
||||
_ANIMATION_START_STATE_STYLES_VAR,
|
||||
_ANIMATION_END_STATE_STYLES_VAR
|
||||
])
|
||||
])
|
||||
.toStmt()
|
||||
])])
|
||||
.toStmt());
|
||||
|
||||
statements.push(o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer))
|
||||
.instantiate([_PREVIOUS_ANIMATION_PLAYERS])
|
||||
.callMethod('destroy', [])
|
||||
.toStmt());
|
||||
|
||||
// before we start any animation we want to clear out the starting
|
||||
// styles from the element's style property (since they were placed
|
||||
// there at the end of the last animation
|
||||
statements.push(RENDER_STYLES_FN
|
||||
.callFn([
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(createIdentifier(Identifiers.clearStyles))
|
||||
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
||||
])
|
||||
.toStmt());
|
||||
|
||||
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
|
||||
.callMethod(
|
||||
'queueAnimation',
|
||||
@ -279,7 +292,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
.toStmt());
|
||||
|
||||
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_TIME_VAR
|
||||
])));
|
||||
@ -288,12 +301,12 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
[
|
||||
new o.FnParam(
|
||||
_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_CURRENT_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 {
|
||||
@ -304,7 +317,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
const lookupMap: any[] = [];
|
||||
Object.keys(context.stateMap.states).forEach(stateName => {
|
||||
const value = context.stateMap.states[stateName];
|
||||
let variableValue = EMPTY_MAP;
|
||||
let variableValue = _EMPTY_MAP;
|
||||
if (isPresent(value)) {
|
||||
const styleMap: any[] = [];
|
||||
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
|
||||
@ -324,6 +337,7 @@ class _AnimationBuilderContext {
|
||||
stateMap = new _AnimationBuilderStateMap();
|
||||
endStateAnimateStep: AnimationStepAst = null;
|
||||
isExpectingFirstStyleStep = false;
|
||||
isExpectingFirstAnimateStep = false;
|
||||
totalTransitionTime = 0;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
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 {isBlank, isPresent} from '../facade/lang';
|
||||
import {ParseError} from '../parse_util';
|
||||
@ -41,7 +41,7 @@ export class AnimationParser {
|
||||
|
||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||
const errors: string[] = [];
|
||||
const componentName = component.type.name;
|
||||
const componentName = identifierName(component.type);
|
||||
const animationTriggerNames = new Set<string>();
|
||||
const asts = component.template.animations.map(entry => {
|
||||
const result = this.parseEntry(entry);
|
||||
|
@ -8,84 +8,29 @@
|
||||
|
||||
import {SchemaMetadata} from '@angular/core';
|
||||
|
||||
import {AnimationCompiler} from './animation/animation_compiler';
|
||||
import {AnimationParser} from './animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
||||
import {DirectiveNormalizer} from './directive_normalizer';
|
||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from './directive_wrapper_compiler';
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||
import {CompileMetadataResolver} from './metadata_resolver';
|
||||
import {NgModuleCompiler} from './ng_module_compiler';
|
||||
import {OutputEmitter} from './output/abstract_emitter';
|
||||
import * as o from './output/output_ast';
|
||||
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
||||
import {TemplateParser} from './template_parser/template_parser';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from './view_compiler/view_compiler';
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
import {AnimationParser} from '../animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {OutputEmitter} from '../output/abstract_emitter';
|
||||
import * as o from '../output/output_ast';
|
||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||
import {TemplateParser} from '../template_parser/template_parser';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||
|
||||
export class SourceModule {
|
||||
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
|
||||
}
|
||||
import {AotCompilerOptions} from './compiler_options';
|
||||
import {GeneratedFile} from './generated_file';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
import {AotSummaryResolver} from './summary_resolver';
|
||||
import {filterFileByPatterns} from './utils';
|
||||
|
||||
// Returns all the source files and a mapping from modules to directives
|
||||
export function analyzeNgModules(
|
||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||
metadataResolver: CompileMetadataResolver): Promise<{
|
||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
|
||||
}> {
|
||||
return _loadNgModules(programStaticSymbols, options, metadataResolver).then(_analyzeNgModules);
|
||||
}
|
||||
|
||||
function _analyzeNgModules(ngModuleMetas: CompileNgModuleMetadata[]) {
|
||||
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
|
||||
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||
const filePaths = new Set<string>();
|
||||
|
||||
// Looping over all modules to construct:
|
||||
// - a map from file to modules `ngModulesByFile`,
|
||||
// - a map from file to directives `ngDirectivesByFile`,
|
||||
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
||||
ngModuleMetas.forEach((ngModuleMeta) => {
|
||||
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
||||
filePaths.add(srcFileUrl);
|
||||
ngModulesByFile.set(
|
||||
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
|
||||
|
||||
ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||
const fileUrl = dirIdentifier.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngDirectivesByFile.set(
|
||||
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference));
|
||||
ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta);
|
||||
});
|
||||
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
||||
const fileUrl = pipeIdentifier.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
||||
});
|
||||
});
|
||||
|
||||
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
||||
|
||||
filePaths.forEach((srcUrl) => {
|
||||
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||
files.push({srcUrl, directives, ngModules});
|
||||
});
|
||||
|
||||
return {
|
||||
// map directive/pipe to module
|
||||
ngModuleByPipeOrDirective,
|
||||
// list modules and directives for every source file
|
||||
files,
|
||||
};
|
||||
}
|
||||
|
||||
export class OfflineCompiler {
|
||||
export class AotCompiler {
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
@ -93,29 +38,45 @@ export class OfflineCompiler {
|
||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||
private _localeId: string, private _translationFormat: string,
|
||||
private _animationParser: AnimationParser) {}
|
||||
private _summaryResolver: AotSummaryResolver, private _localeId: string,
|
||||
private _translationFormat: string, private _animationParser: AnimationParser,
|
||||
private _staticReflector: StaticReflector, private _options: AotCompilerOptions) {}
|
||||
|
||||
clearCache() { this._metadataResolver.clearCache(); }
|
||||
|
||||
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
|
||||
Promise<SourceModule[]> {
|
||||
return analyzeNgModules(staticSymbols, options, this._metadataResolver)
|
||||
.then(({ngModuleByPipeOrDirective, files}) => {
|
||||
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
|
||||
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
||||
return Promise
|
||||
.all(ngModules.map(
|
||||
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||
ngModule.type.reference, false)))
|
||||
.then(() => {
|
||||
const sourceModules = files.map(
|
||||
file => this._compileSrcFile(
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
|
||||
file.ngModules));
|
||||
return ListWrapper.flatten(sourceModules);
|
||||
});
|
||||
}
|
||||
|
||||
private _compileSrcFile(
|
||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
|
||||
directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||
ngModules: StaticSymbol[]): GeneratedFile[] {
|
||||
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||
const statements: o.Statement[] = [];
|
||||
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
|
||||
exportedVars.push(
|
||||
@ -134,7 +95,7 @@ export class OfflineCompiler {
|
||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||
if (!ngModule) {
|
||||
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);
|
||||
@ -142,7 +103,7 @@ export class OfflineCompiler {
|
||||
// compile styles
|
||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||
generatedFiles.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||
});
|
||||
|
||||
// compile components
|
||||
@ -155,9 +116,9 @@ export class OfflineCompiler {
|
||||
if (statements.length > 0) {
|
||||
const srcModule = this._codegenSourceModule(
|
||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||
outputSourceModules.unshift(srcModule);
|
||||
generatedFiles.unshift(srcModule);
|
||||
}
|
||||
return outputSourceModules;
|
||||
return generatedFiles;
|
||||
}
|
||||
|
||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||
@ -165,24 +126,24 @@ export class OfflineCompiler {
|
||||
const providers: CompileProviderMetadata[] = [];
|
||||
|
||||
if (this._localeId) {
|
||||
providers.push(new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
|
||||
providers.push({
|
||||
token: createIdentifierToken(Identifiers.LOCALE_ID),
|
||||
useValue: this._localeId,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (this._translationFormat) {
|
||||
providers.push(new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||
providers.push({
|
||||
token: createIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||
useValue: this._translationFormat
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||
|
||||
appCompileResult.dependencies.forEach((dep) => {
|
||||
dep.placeholder.name = _componentFactoryName(dep.comp);
|
||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
|
||||
dep.placeholder.reference = this._staticReflector.getStaticSymbol(
|
||||
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
|
||||
});
|
||||
|
||||
targetStatements.push(...appCompileResult.statements);
|
||||
@ -201,14 +162,17 @@ export class OfflineCompiler {
|
||||
private _compileComponentFactory(
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: 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(
|
||||
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||
targetStatements.push(
|
||||
o.variable(compFactoryVar)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
|
||||
compMeta.type)])
|
||||
.set(o.importExpr(
|
||||
createIdentifier(Identifiers.ComponentFactory), [o.importType(compMeta.type)])
|
||||
.instantiate(
|
||||
[
|
||||
o.literal(compMeta.selector),
|
||||
@ -216,7 +180,7 @@ export class OfflineCompiler {
|
||||
o.importExpr(compMeta.type),
|
||||
],
|
||||
o.importType(
|
||||
resolveIdentifier(Identifiers.ComponentFactory),
|
||||
createIdentifier(Identifiers.ComponentFactory),
|
||||
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
return compFactoryVar;
|
||||
@ -234,23 +198,24 @@ export class OfflineCompiler {
|
||||
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||
compMeta.type.name);
|
||||
identifierName(compMeta.type));
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const viewResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||
if (componentStyles) {
|
||||
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
||||
targetStatements.push(
|
||||
..._resolveStyleStatements(this._staticReflector, componentStyles, fileSuffix));
|
||||
}
|
||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||
targetStatements.push(..._resolveViewStatements(viewResult));
|
||||
targetStatements.push(..._resolveViewStatements(this._staticReflector, viewResult));
|
||||
return viewResult.viewClassVar;
|
||||
}
|
||||
|
||||
private _codgenStyles(
|
||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
||||
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
|
||||
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
|
||||
return this._codegenSourceModule(
|
||||
fileUrl, _stylesModuleUrl(
|
||||
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
||||
@ -258,26 +223,29 @@ export class OfflineCompiler {
|
||||
}
|
||||
|
||||
private _codegenSourceModule(
|
||||
fileUrl: string, moduleUrl: string, statements: o.Statement[],
|
||||
exportedVars: string[]): SourceModule {
|
||||
return new SourceModule(
|
||||
fileUrl, moduleUrl,
|
||||
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
||||
srcFileUrl: string, genFileUrl: string, statements: o.Statement[],
|
||||
exportedVars: string[]): GeneratedFile {
|
||||
return new GeneratedFile(
|
||||
srcFileUrl, genFileUrl,
|
||||
this._outputEmitter.emitStatements(genFileUrl, statements, exportedVars));
|
||||
}
|
||||
}
|
||||
|
||||
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
||||
function _resolveViewStatements(
|
||||
reflector: StaticReflector, compileResult: ViewCompileResult): o.Statement[] {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
if (dep instanceof ViewClassDependency) {
|
||||
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) {
|
||||
const cfd = <ComponentFactoryDependency>dep;
|
||||
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
||||
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
||||
cfd.placeholder.reference = reflector.getStaticSymbol(
|
||||
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
|
||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||
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;
|
||||
@ -285,9 +253,11 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
|
||||
|
||||
|
||||
function _resolveStyleStatements(
|
||||
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] {
|
||||
reflector: StaticReflector, compileResult: CompiledStylesheet,
|
||||
fileSuffix: string): o.Statement[] {
|
||||
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;
|
||||
}
|
||||
@ -298,7 +268,7 @@ function _ngfactoryModuleUrl(dirUrl: string): string {
|
||||
}
|
||||
|
||||
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
|
||||
return `${comp.name}NgFactory`;
|
||||
return `${identifierName(comp)}NgFactory`;
|
||||
}
|
||||
|
||||
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
||||
@ -307,7 +277,8 @@ function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string):
|
||||
|
||||
function _assertComponent(meta: CompileDirectiveMetadata) {
|
||||
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.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,31 +296,153 @@ function _splitTypescriptSuffix(path: string): string[] {
|
||||
return [path, ''];
|
||||
}
|
||||
|
||||
export interface NgAnalyzedModules {
|
||||
ngModules: CompileNgModuleMetadata[];
|
||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||
files: Array<{
|
||||
srcUrl: string,
|
||||
directives: StaticSymbol[],
|
||||
pipes: StaticSymbol[],
|
||||
ngModules: StaticSymbol[]
|
||||
}>;
|
||||
symbolsMissingModule?: StaticSymbol[];
|
||||
}
|
||||
|
||||
// Returns all the source files and a mapping from modules to directives
|
||||
export function analyzeNgModules(
|
||||
programStaticSymbols: StaticSymbol[],
|
||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
|
||||
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||
const {ngModules, symbolsMissingModule} =
|
||||
_createNgModules(programStaticSymbols, options, metadataResolver);
|
||||
return _analyzeNgModules(ngModules, symbolsMissingModule);
|
||||
}
|
||||
|
||||
export function analyzeAndValidateNgModules(
|
||||
programStaticSymbols: StaticSymbol[],
|
||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
|
||||
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||
const result = analyzeNgModules(programStaticSymbols, options, metadataResolver);
|
||||
if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
|
||||
const messages = result.symbolsMissingModule.map(
|
||||
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
||||
throw new Error(messages.join('\n'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _analyzeNgModules(
|
||||
ngModuleMetas: CompileNgModuleMetadata[],
|
||||
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
|
||||
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
|
||||
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||
const ngPipesByFile = new Map<string, StaticSymbol[]>();
|
||||
const filePaths = new Set<string>();
|
||||
|
||||
// Looping over all modules to construct:
|
||||
// - a map from file to modules `ngModulesByFile`,
|
||||
// - a map from file to directives `ngDirectivesByFile`,
|
||||
// - a map from file to pipes `ngPipesByFile`,
|
||||
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
||||
ngModuleMetas.forEach((ngModuleMeta) => {
|
||||
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
||||
filePaths.add(srcFileUrl);
|
||||
ngModulesByFile.set(
|
||||
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
|
||||
|
||||
ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||
const fileUrl = dirIdentifier.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngDirectivesByFile.set(
|
||||
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference));
|
||||
ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta);
|
||||
});
|
||||
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
||||
const fileUrl = pipeIdentifier.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngPipesByFile.set(
|
||||
fileUrl, (ngPipesByFile.get(fileUrl) || []).concat(pipeIdentifier.reference));
|
||||
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
||||
});
|
||||
});
|
||||
|
||||
const files:
|
||||
{srcUrl: string,
|
||||
directives: StaticSymbol[],
|
||||
pipes: StaticSymbol[],
|
||||
ngModules: StaticSymbol[]}[] = [];
|
||||
|
||||
filePaths.forEach((srcUrl) => {
|
||||
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||
const pipes = ngPipesByFile.get(srcUrl) || [];
|
||||
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||
files.push({srcUrl, directives, pipes, ngModules});
|
||||
});
|
||||
|
||||
return {
|
||||
// map directive/pipe to module
|
||||
ngModuleByPipeOrDirective,
|
||||
// list modules and directives for every source file
|
||||
files,
|
||||
ngModules: ngModuleMetas, symbolsMissingModule
|
||||
};
|
||||
}
|
||||
|
||||
export function extractProgramSymbols(
|
||||
staticReflector: StaticReflector, files: string[],
|
||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
|
||||
const staticSymbols: StaticSymbol[] = [];
|
||||
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
||||
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
||||
if (!moduleMetadata) {
|
||||
console.error(`WARNING: no metadata found for ${sourceFile}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const metadata = moduleMetadata['metadata'];
|
||||
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const symbol of Object.keys(metadata)) {
|
||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
||||
// Ignore symbols that are only included to record error information.
|
||||
continue;
|
||||
}
|
||||
staticSymbols.push(staticReflector.getStaticSymbol(sourceFile, symbol));
|
||||
}
|
||||
});
|
||||
|
||||
return staticSymbols;
|
||||
}
|
||||
|
||||
// Load the NgModules and check
|
||||
// that all directives / pipes that are present in the program
|
||||
// are also declared by a module.
|
||||
function _loadNgModules(
|
||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||
metadataResolver: CompileMetadataResolver): Promise<CompileNgModuleMetadata[]> {
|
||||
function _createNgModules(
|
||||
programStaticSymbols: StaticSymbol[],
|
||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp},
|
||||
metadataResolver: CompileMetadataResolver):
|
||||
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
|
||||
const ngModules = new Map<any, CompileNgModuleMetadata>();
|
||||
const programPipesAndDirectives: StaticSymbol[] = [];
|
||||
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
||||
const loadingPromises: Promise<any>[] = [];
|
||||
|
||||
const addNgModule = (staticSymbol: any) => {
|
||||
if (ngModules.has(staticSymbol)) {
|
||||
if (ngModules.has(staticSymbol) || !filterFileByPatterns(staticSymbol.filePath, options)) {
|
||||
return false;
|
||||
}
|
||||
const {ngModule, loading} = metadataResolver.loadNgModuleMetadata(staticSymbol, false, false);
|
||||
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
||||
if (ngModule) {
|
||||
ngModules.set(ngModule.type.reference, ngModule);
|
||||
loadingPromises.push(loading);
|
||||
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
|
||||
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
|
||||
if (options.transitiveModules) {
|
||||
// For every input modules add the list of transitively included modules
|
||||
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
|
||||
}
|
||||
// For every input module add the list of transitively included modules
|
||||
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.reference));
|
||||
}
|
||||
return !!ngModule;
|
||||
};
|
||||
@ -364,11 +457,5 @@ function _loadNgModules(
|
||||
const symbolsMissingModule =
|
||||
programPipesAndDirectives.filter(s => !ngModulePipesAndDirective.has(s));
|
||||
|
||||
if (symbolsMissingModule.length) {
|
||||
const messages = symbolsMissingModule.map(
|
||||
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
||||
throw new Error(messages.join('\n'));
|
||||
}
|
||||
|
||||
return Promise.all(loadingPromises).then(() => Array.from(ngModules.values()));
|
||||
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
||||
}
|
76
modules/@angular/compiler/src/aot/compiler_factory.ts
Normal file
76
modules/@angular/compiler/src/aot/compiler_factory.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @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 {ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {AnimationParser} from '../animation/animation_parser';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveResolver} from '../directive_resolver';
|
||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {Lexer} from '../expression_parser/lexer';
|
||||
import {Parser} from '../expression_parser/parser';
|
||||
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {HtmlParser} from '../ml_parser/html_parser';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {NgModuleResolver} from '../ng_module_resolver';
|
||||
import {TypeScriptEmitter} from '../output/ts_emitter';
|
||||
import {PipeResolver} from '../pipe_resolver';
|
||||
import {Console} from '../private_import_core';
|
||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||
import {StyleCompiler} from '../style_compiler';
|
||||
import {TemplateParser} from '../template_parser/template_parser';
|
||||
import {createOfflineCompileUrlResolver} from '../url_resolver';
|
||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||
|
||||
import {AotCompiler} from './compiler';
|
||||
import {AotCompilerHost} from './compiler_host';
|
||||
import {AotCompilerOptions} from './compiler_options';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {AotSummaryResolver} from './summary_resolver';
|
||||
|
||||
/**
|
||||
* Creates a new AotCompiler based on options and a host.
|
||||
*/
|
||||
export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCompilerOptions):
|
||||
{compiler: AotCompiler, reflector: StaticReflector} {
|
||||
let translations: string = options.translations || '';
|
||||
|
||||
const urlResolver = createOfflineCompileUrlResolver();
|
||||
const staticReflector = new StaticReflector(compilerHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat);
|
||||
const config = new CompilerConfig({
|
||||
genDebugInfo: options.debug === true,
|
||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||
logBindingUpdate: false,
|
||||
useJit: false
|
||||
});
|
||||
const normalizer = new DirectiveNormalizer(
|
||||
{get: (url: string) => compilerHost.loadResource(url)}, urlResolver, htmlParser, config);
|
||||
const expressionParser = new Parser(new Lexer());
|
||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||
const console = new Console();
|
||||
const tmplParser =
|
||||
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
||||
const summaryResolver = new AotSummaryResolver(compilerHost, staticReflector, options);
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||
staticReflector);
|
||||
// TODO(vicb): do not pass options.i18nFormat here
|
||||
const compiler = new AotCompiler(
|
||||
resolver, tmplParser, new StyleCompiler(urlResolver),
|
||||
new ViewCompiler(config, elementSchemaRegistry),
|
||||
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
|
||||
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
|
||||
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
||||
return {compiler, reflector: staticReflector};
|
||||
}
|
25
modules/@angular/compiler/src/aot/compiler_host.ts
Normal file
25
modules/@angular/compiler/src/aot/compiler_host.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @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 {ImportResolver} from '../output/path_util';
|
||||
|
||||
import {StaticReflectorHost} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
import {AotSummaryResolverHost} from './summary_resolver';
|
||||
|
||||
/**
|
||||
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
|
||||
* services and from underlying file systems.
|
||||
*/
|
||||
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver,
|
||||
AotSummaryResolverHost {
|
||||
/**
|
||||
* Loads a resource (e.g. html / css)
|
||||
*/
|
||||
loadResource(path: string): Promise<string>;
|
||||
}
|
16
modules/@angular/compiler/src/aot/compiler_options.ts
Normal file
16
modules/@angular/compiler/src/aot/compiler_options.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @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 interface AotCompilerOptions {
|
||||
debug?: boolean;
|
||||
locale?: string;
|
||||
i18nFormat?: string;
|
||||
translations?: string;
|
||||
includeFilePattern?: RegExp;
|
||||
excludeFilePattern?: RegExp;
|
||||
}
|
@ -6,8 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This module provides a set of common Pipes.
|
||||
*/
|
||||
export class GeneratedFile {
|
||||
constructor(public srcFileUrl: string, public genFileUrl: string, public source: string) {}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from './private_import_core';
|
||||
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
|
||||
export class StaticAndDynamicReflectionCapabilities {
|
@ -7,54 +7,60 @@
|
||||
*/
|
||||
|
||||
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
import {ReflectorReader} from '../private_import_core';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
|
||||
import {ReflectorReader} from './private_import_core';
|
||||
|
||||
const SUPPORTED_SCHEMA_VERSION = 1;
|
||||
const SUPPORTED_SCHEMA_VERSION = 2;
|
||||
const ANGULAR_IMPORT_LOCATIONS = {
|
||||
coreDecorators: '@angular/core/src/metadata',
|
||||
diDecorators: '@angular/core/src/di/metadata',
|
||||
diMetadata: '@angular/core/src/di/metadata',
|
||||
diOpaqueToken: '@angular/core/src/di/opaque_token',
|
||||
animationMetadata: '@angular/core/src/animation/metadata',
|
||||
provider: '@angular/core/src/di/provider'
|
||||
};
|
||||
|
||||
/**
|
||||
* The host of the static resolver is expected to be able to provide module metadata in the form of
|
||||
* ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
|
||||
* produced and the module has exported variables or classes with decorators. Module metadata can
|
||||
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
|
||||
* The host of the StaticReflector disconnects the implementation from TypeScript / other language
|
||||
* services and from underlying file systems.
|
||||
*/
|
||||
export interface StaticReflectorHost {
|
||||
/**
|
||||
* Return a ModuleMetadata for the given module.
|
||||
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
|
||||
* produced and the module has exported variables or classes with decorators. Module metadata can
|
||||
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
|
||||
*
|
||||
* @param modulePath is a string identifier for a module as an absolute path.
|
||||
* @returns the metadata for the given module.
|
||||
*/
|
||||
getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[];
|
||||
getMetadataFor(modulePath: string): {[key: string]: any}[];
|
||||
|
||||
/**
|
||||
* Resolve a symbol from an import statement form, to the file where it is declared.
|
||||
* @param module the location imported from
|
||||
* @param containingFile for relative imports, the path of the file containing the import
|
||||
* Converts a module name that is used in an `import` to a file path.
|
||||
* I.e.
|
||||
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
|
||||
*/
|
||||
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol;
|
||||
|
||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol;
|
||||
|
||||
angularImportLocations(): {
|
||||
coreDecorators: string,
|
||||
diDecorators: string,
|
||||
diMetadata: string,
|
||||
diOpaqueToken: string,
|
||||
animationMetadata: string,
|
||||
provider: string
|
||||
};
|
||||
|
||||
getCanonicalFileName(fileName: string): string;
|
||||
moduleNameToFileName(moduleName: string, containingFile: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* This token is unique for a filePath and name and can be used as a hash table key.
|
||||
* A cache of static symbol used by the StaticReflector to return the same symbol for the
|
||||
* same symbol values.
|
||||
*/
|
||||
export class StaticSymbol {
|
||||
constructor(public filePath: string, public name: string, public members?: string[]) {}
|
||||
export class StaticSymbolCache {
|
||||
private cache = new Map<string, StaticSymbol>();
|
||||
|
||||
get(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||
const memberSuffix = members ? `.${ members.join('.')}` : '';
|
||||
const key = `"${declarationFile}".${name}${memberSuffix}`;
|
||||
let result = this.cache.get(key);
|
||||
if (!result) {
|
||||
result = new StaticSymbol(declarationFile, name, members);
|
||||
this.cache.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,55 +68,85 @@ export class StaticSymbol {
|
||||
* templates statically.
|
||||
*/
|
||||
export class StaticReflector implements ReflectorReader {
|
||||
private declarationCache = new Map<string, StaticSymbol>();
|
||||
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 methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
private opaqueToken: StaticSymbol;
|
||||
|
||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||
constructor(
|
||||
private host: StaticReflectorHost,
|
||||
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();
|
||||
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 {
|
||||
const staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||
const staticSymbol = this.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||
return staticSymbol ? staticSymbol.filePath : null;
|
||||
}
|
||||
|
||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
||||
return this.host.findDeclaration(moduleUrl, name, '');
|
||||
return this.findDeclaration(moduleUrl, name, '');
|
||||
}
|
||||
|
||||
resolveEnum(enumIdentifier: any, name: string): any {
|
||||
const staticSymbol: StaticSymbol = enumIdentifier;
|
||||
return this.host.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]);
|
||||
return this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]);
|
||||
}
|
||||
|
||||
public annotations(type: StaticSymbol): any[] {
|
||||
let annotations = this.annotationCache.get(type);
|
||||
if (!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']) {
|
||||
annotations = this.simplify(type, classMetadata['decorators']);
|
||||
} else {
|
||||
annotations = [];
|
||||
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
|
||||
annotations.push(...ownAnnotations);
|
||||
}
|
||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!propMetadata) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : {};
|
||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||
const classMetadata = this.getTypeMetadata(type) || {};
|
||||
propMetadata = {};
|
||||
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)
|
||||
.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']) {
|
||||
return this.simplify(type, prop['decorators']);
|
||||
} else {
|
||||
return [];
|
||||
decorators.push(...this.simplify(type, prop['decorators']));
|
||||
}
|
||||
});
|
||||
this.propertyCache.set(type, propMetadata);
|
||||
@ -120,7 +156,10 @@ export class StaticReflector implements ReflectorReader {
|
||||
|
||||
public parameters(type: StaticSymbol): any[] {
|
||||
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 {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
@ -132,7 +171,6 @@ export class StaticReflector implements ReflectorReader {
|
||||
const ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||
const parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
||||
const parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
||||
|
||||
parameters = [];
|
||||
parameterTypes.forEach((paramType, index) => {
|
||||
const nestedResult: any[] = [];
|
||||
@ -145,6 +183,8 @@ export class StaticReflector implements ReflectorReader {
|
||||
}
|
||||
parameters.push(nestedResult);
|
||||
});
|
||||
} else if (classMetadata['extends']) {
|
||||
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
|
||||
}
|
||||
if (!parameters) {
|
||||
parameters = [];
|
||||
@ -153,91 +193,221 @@ export class StaticReflector implements ReflectorReader {
|
||||
}
|
||||
return parameters;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(
|
||||
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
private _methodNames(type: any): {[key: string]: boolean} {
|
||||
let methodNames = this.methodCache.get(type);
|
||||
if (!methodNames) {
|
||||
const classMetadata = this.getTypeMetadata(type) || {};
|
||||
methodNames = {};
|
||||
if (classMetadata['extends']) {
|
||||
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
||||
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : null;
|
||||
const member: any[] =
|
||||
members && members.hasOwnProperty(lcProperty) ? members[lcProperty] : null;
|
||||
return member ? member.some(a => a['__symbolic'] == 'method') : false;
|
||||
return methodNames;
|
||||
}
|
||||
|
||||
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private initializeConversionMap(): void {
|
||||
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
|
||||
this.host.angularImportLocations();
|
||||
this.opaqueToken = this.host.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
||||
ANGULAR_IMPORT_LOCATIONS;
|
||||
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
||||
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Self'), Self);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Input'), Input);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Output'), Output);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Directive'), Directive);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Component'), Component);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'NgModule'), NgModule);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'Directive'), Directive);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'Component'), Component);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'NgModule'), NgModule);
|
||||
|
||||
// Note: Some metadata classes can be used directly with Provider.deps.
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Self'), Self);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'Optional'), Optional);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
|
||||
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'state'), state);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'transition'), transition);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'style'), style);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'animate'), animate);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'group'), group);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
|
||||
}
|
||||
|
||||
/**
|
||||
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
||||
* All types passed to the StaticResolver should be pseudo-types returned by this method.
|
||||
*
|
||||
* @param declarationFile the absolute path of the file where the symbol is declared
|
||||
* @param name the name of the type.
|
||||
*/
|
||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||
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 {
|
||||
const resolveModule = (moduleName: string): string => {
|
||||
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
||||
if (!resolvedModulePath) {
|
||||
this.reportError(
|
||||
new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`),
|
||||
null, filePath);
|
||||
}
|
||||
return resolvedModulePath;
|
||||
};
|
||||
const cacheKey = `${filePath}|${symbolName}`;
|
||||
let staticSymbol = this.declarationCache.get(cacheKey);
|
||||
if (staticSymbol) {
|
||||
return staticSymbol;
|
||||
}
|
||||
const metadata = this.getModuleMetadata(filePath);
|
||||
if (metadata) {
|
||||
// If we have metadata for the symbol, this is the original exporting location.
|
||||
if (metadata['metadata'][symbolName]) {
|
||||
staticSymbol = this.getStaticSymbol(filePath, symbolName);
|
||||
}
|
||||
|
||||
// If no, try to find the symbol in one of the re-export location
|
||||
if (!staticSymbol && metadata['exports']) {
|
||||
// Try and find the symbol in the list of explicitly re-exported symbols.
|
||||
for (const moduleExport of metadata['exports']) {
|
||||
if (moduleExport.export) {
|
||||
const exportSymbol = moduleExport.export.find((symbol: any) => {
|
||||
if (typeof symbol === 'string') {
|
||||
return symbol == symbolName;
|
||||
} else {
|
||||
return symbol.as == symbolName;
|
||||
}
|
||||
});
|
||||
if (exportSymbol) {
|
||||
let symName = symbolName;
|
||||
if (typeof exportSymbol !== 'string') {
|
||||
symName = exportSymbol.name;
|
||||
}
|
||||
const resolvedModule = resolveModule(moduleExport.from);
|
||||
if (resolvedModule) {
|
||||
staticSymbol =
|
||||
this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!staticSymbol) {
|
||||
// Try to find the symbol via export * directives.
|
||||
for (const moduleExport of metadata['exports']) {
|
||||
if (!moduleExport.export) {
|
||||
const resolvedModule = resolveModule(moduleExport.from);
|
||||
if (resolvedModule) {
|
||||
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
||||
if (candidateSymbol) {
|
||||
staticSymbol = candidateSymbol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.declarationCache.set(cacheKey, staticSymbol);
|
||||
return staticSymbol;
|
||||
}
|
||||
|
||||
findDeclaration(module: string, symbolName: string, containingFile?: string): StaticSymbol {
|
||||
try {
|
||||
const filePath = this.host.moduleNameToFileName(module, containingFile);
|
||||
let symbol: StaticSymbol;
|
||||
if (!filePath) {
|
||||
// If the file cannot be found the module is probably referencing a declared module
|
||||
// for which there is no disambiguating file and we also don't need to track
|
||||
// re-exports. Just use the module name.
|
||||
symbol = this.getStaticSymbol(module, symbolName);
|
||||
} else {
|
||||
symbol = this.resolveExportedSymbol(filePath, symbolName) ||
|
||||
this.getStaticSymbol(filePath, symbolName);
|
||||
}
|
||||
return symbol;
|
||||
} catch (e) {
|
||||
console.error(`can't resolve module ${module} from ${containingFile}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public simplify(context: StaticSymbol, value: any): any {
|
||||
const _this = this;
|
||||
const self = this;
|
||||
let scope = BindingScope.empty;
|
||||
const calling = new Map<StaticSymbol, boolean>();
|
||||
|
||||
@ -245,16 +415,16 @@ export class StaticReflector implements ReflectorReader {
|
||||
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
||||
let staticSymbol: StaticSymbol;
|
||||
if (expression['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(
|
||||
expression['module'], expression['name'], context.filePath);
|
||||
staticSymbol =
|
||||
self.findDeclaration(expression['module'], expression['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']);
|
||||
staticSymbol = self.getStaticSymbol(context.filePath, expression['name']);
|
||||
}
|
||||
return staticSymbol;
|
||||
}
|
||||
|
||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||
const moduleMetadata = self.getModuleMetadata(staticSymbol.filePath);
|
||||
const declarationValue =
|
||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||
return declarationValue;
|
||||
@ -264,7 +434,7 @@ export class StaticReflector implements ReflectorReader {
|
||||
if (value && value.__symbolic === 'new' && value.expression) {
|
||||
const target = value.expression;
|
||||
if (target.__symbolic == 'reference') {
|
||||
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
||||
return sameSymbol(resolveReference(context, target), self.opaqueToken);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -446,23 +616,26 @@ export class StaticReflector implements ReflectorReader {
|
||||
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||
return null;
|
||||
case 'select':
|
||||
let selectContext = context;
|
||||
let selectTarget = simplify(expression['expression']);
|
||||
if (selectTarget instanceof StaticSymbol) {
|
||||
// Access to a static instance variable
|
||||
const member: string = expression['member'];
|
||||
const members = selectTarget.members ?
|
||||
(selectTarget.members as string[]).concat(member) :
|
||||
[member];
|
||||
const declarationValue = resolveReferenceValue(selectTarget);
|
||||
selectContext =
|
||||
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
||||
if (declarationValue && declarationValue.statics) {
|
||||
selectTarget = declarationValue.statics;
|
||||
} else {
|
||||
const member: string = expression['member'];
|
||||
const members = selectTarget.members ?
|
||||
(selectTarget.members as string[]).concat(member) :
|
||||
[member];
|
||||
return _this.host.getStaticSymbol(
|
||||
selectTarget.filePath, selectTarget.name, members);
|
||||
return selectContext;
|
||||
}
|
||||
}
|
||||
const member = simplify(expression['member']);
|
||||
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]);
|
||||
const member = simplifyInContext(selectContext, expression['member'], depth + 1);
|
||||
if (selectTarget && isPrimitive(member))
|
||||
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
|
||||
return null;
|
||||
case 'reference':
|
||||
if (!expression.module) {
|
||||
@ -493,12 +666,12 @@ export class StaticReflector implements ReflectorReader {
|
||||
// Determine if the function is a built-in conversion
|
||||
let target = expression['expression'];
|
||||
if (target['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(
|
||||
target['module'], target['name'], context.filePath);
|
||||
staticSymbol =
|
||||
self.findDeclaration(target['module'], target['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.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) {
|
||||
let args: any[] = expression['arguments'];
|
||||
if (!args) {
|
||||
@ -539,7 +712,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)) {
|
||||
return undefined;
|
||||
}
|
||||
@ -552,18 +734,25 @@ export class StaticReflector implements ReflectorReader {
|
||||
public getModuleMetadata(module: string): {[key: string]: any} {
|
||||
let moduleMetadata = this.metadataCache.get(module);
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata = this.host.getMetadataFor(module);
|
||||
if (Array.isArray(moduleMetadata)) {
|
||||
moduleMetadata = moduleMetadata.find(md => md['version'] === SUPPORTED_SCHEMA_VERSION) ||
|
||||
moduleMetadata[0];
|
||||
const moduleMetadatas = this.host.getMetadataFor(module);
|
||||
if (moduleMetadatas) {
|
||||
let maxVersion = -1;
|
||||
moduleMetadatas.forEach((md) => {
|
||||
if (md['version'] > maxVersion) {
|
||||
maxVersion = md['version'];
|
||||
moduleMetadata = md;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata =
|
||||
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
||||
}
|
||||
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
||||
throw new Error(
|
||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
|
||||
this.reportError(
|
||||
new Error(
|
||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`),
|
||||
null);
|
||||
}
|
||||
this.metadataCache.set(module, moduleMetadata);
|
||||
}
|
||||
@ -601,6 +790,7 @@ function expandedMessage(error: any): string {
|
||||
if (error.context && error.context.name) {
|
||||
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return error.message;
|
||||
}
|
16
modules/@angular/compiler/src/aot/static_symbol.ts
Normal file
16
modules/@angular/compiler/src/aot/static_symbol.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* This token is unique for a filePath and name and can be used as a hash table key.
|
||||
*/
|
||||
export class StaticSymbol {
|
||||
constructor(public filePath: string, public name: string, public members?: string[]) {}
|
||||
}
|
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 {StaticSymbol} from './aot/static_symbol';
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {LifecycleHooks} from './private_import_core';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {LifecycleHooks, reflector} from './private_import_core';
|
||||
import {CssSelector} from './selector';
|
||||
import {sanitizeIdentifier, splitAtColon} from './util';
|
||||
import {splitAtColon} from './util';
|
||||
|
||||
function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
@ -24,10 +25,6 @@ function unimplemented(): any {
|
||||
// group 3: "@trigger" from "@trigger"
|
||||
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
||||
|
||||
export abstract class CompileMetadataWithIdentifier {
|
||||
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
||||
}
|
||||
|
||||
export class CompileAnimationEntryMetadata {
|
||||
constructor(
|
||||
public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
|
||||
@ -78,24 +75,50 @@ export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMeta
|
||||
constructor(steps: CompileAnimationMetadata[] = null) { super(steps); }
|
||||
}
|
||||
|
||||
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
|
||||
reference: any;
|
||||
name: string;
|
||||
prefix: string;
|
||||
moduleUrl: string;
|
||||
value: any;
|
||||
|
||||
constructor(
|
||||
{reference, name, moduleUrl, prefix, value}:
|
||||
{reference?: any, name?: string, moduleUrl?: string, prefix?: string, value?: any} = {}) {
|
||||
this.reference = reference;
|
||||
this.name = name;
|
||||
this.prefix = prefix;
|
||||
this.moduleUrl = moduleUrl;
|
||||
this.value = value;
|
||||
function _sanitizeIdentifier(name: string): string {
|
||||
return name.replace(/\W/g, '_');
|
||||
}
|
||||
|
||||
let _anonymousTypeIndex = 0;
|
||||
|
||||
export function identifierName(compileIdentifier: CompileIdentifierMetadata): string {
|
||||
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
|
||||
* the directive / module itself.
|
||||
*/
|
||||
export interface CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
export interface CompileSummary { summaryKind: CompileSummaryKind; }
|
||||
|
||||
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 {
|
||||
isAttribute: boolean;
|
||||
isSelf: boolean;
|
||||
isHost: boolean;
|
||||
isSkipSelf: boolean;
|
||||
isOptional: boolean;
|
||||
isValue: boolean;
|
||||
export interface CompileProviderMetadata {
|
||||
token: CompileTokenMetadata;
|
||||
value: any;
|
||||
|
||||
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: {
|
||||
isAttribute?: boolean,
|
||||
isSelf?: boolean,
|
||||
isHost?: 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;
|
||||
}
|
||||
useClass?: CompileTypeMetadata;
|
||||
useValue?: any;
|
||||
useExisting?: CompileTokenMetadata;
|
||||
useFactory?: CompileFactoryMetadata;
|
||||
deps?: CompileDiDependencyMetadata[];
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
export class CompileProviderMetadata {
|
||||
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 {
|
||||
export interface CompileFactoryMetadata extends CompileIdentifierMetadata {
|
||||
diDeps: CompileDiDependencyMetadata[];
|
||||
reference: any;
|
||||
}
|
||||
|
||||
constructor({reference, name, moduleUrl, prefix, diDeps, value}: {
|
||||
reference?: Function,
|
||||
name?: string,
|
||||
prefix?: string,
|
||||
moduleUrl?: string,
|
||||
value?: boolean,
|
||||
diDeps?: CompileDiDependencyMetadata[]
|
||||
}) {
|
||||
super({reference: reference, name: name, prefix: prefix, moduleUrl: moduleUrl, value: value});
|
||||
this.diDeps = _normalizeArray(diDeps);
|
||||
export function tokenName(token: CompileTokenMetadata) {
|
||||
return isPresent(token.value) ? _sanitizeIdentifier(token.value) :
|
||||
identifierName(token.identifier);
|
||||
}
|
||||
|
||||
export function tokenReference(token: CompileTokenMetadata) {
|
||||
if (isPresent(token.identifier)) {
|
||||
return token.identifier.reference;
|
||||
} else {
|
||||
return token.value;
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
||||
value: any;
|
||||
identifier: CompileIdentifierMetadata;
|
||||
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;
|
||||
}
|
||||
export interface CompileTokenMetadata {
|
||||
value?: any;
|
||||
identifier?: CompileIdentifierMetadata|CompileTypeMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a type.
|
||||
*/
|
||||
export class CompileTypeMetadata extends CompileIdentifierMetadata {
|
||||
isHost: boolean;
|
||||
export interface CompileTypeMetadata extends CompileIdentifierMetadata {
|
||||
diDeps: CompileDiDependencyMetadata[];
|
||||
lifecycleHooks: LifecycleHooks[];
|
||||
|
||||
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);
|
||||
}
|
||||
reference: any;
|
||||
}
|
||||
|
||||
export class CompileQueryMetadata {
|
||||
export interface CompileQueryMetadata {
|
||||
selectors: Array<CompileTokenMetadata>;
|
||||
descendants: boolean;
|
||||
first: boolean;
|
||||
propertyName: string;
|
||||
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.
|
||||
*/
|
||||
export interface CompileTemplateSummary extends CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
animations: string[];
|
||||
ngContentSelectors: string[];
|
||||
encapsulation: ViewEncapsulation;
|
||||
@ -325,7 +258,7 @@ export class CompileTemplateMetadata {
|
||||
|
||||
toSummary(): CompileTemplateSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
summaryKind: CompileSummaryKind.Template,
|
||||
animations: this.animations.map(anim => anim.name),
|
||||
ngContentSelectors: this.ngContentSelectors,
|
||||
encapsulation: this.encapsulation
|
||||
@ -333,8 +266,9 @@ export class CompileTemplateMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
export interface CompileDirectiveSummary extends CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
// Note: This should only use interfaces as nested data types
|
||||
// as we need to be able to serialize this from/to JSON!
|
||||
export interface CompileDirectiveSummary extends CompileTypeSummary {
|
||||
type: CompileTypeMetadata;
|
||||
isComponent: boolean;
|
||||
selector: string;
|
||||
@ -355,10 +289,11 @@ export interface CompileDirectiveSummary extends CompileSummary {
|
||||
/**
|
||||
* Metadata regarding compilation of a directive.
|
||||
*/
|
||||
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
export class CompileDirectiveMetadata {
|
||||
static create(
|
||||
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers,
|
||||
viewProviders, queries, viewQueries, entryComponents, template}: {
|
||||
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
|
||||
providers, viewProviders, queries, viewQueries, entryComponents, template}: {
|
||||
isHost?: boolean,
|
||||
type?: CompileTypeMetadata,
|
||||
isComponent?: boolean,
|
||||
selector?: string,
|
||||
@ -367,10 +302,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
inputs?: string[],
|
||||
outputs?: string[],
|
||||
host?: {[key: string]: string},
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
viewProviders?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
providers?: CompileProviderMetadata[],
|
||||
viewProviders?: CompileProviderMetadata[],
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
@ -412,6 +345,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
}
|
||||
|
||||
return new CompileDirectiveMetadata({
|
||||
isHost,
|
||||
type,
|
||||
isComponent: !!isComponent, selector, exportAs, changeDetection,
|
||||
inputs: inputsMap,
|
||||
@ -427,6 +361,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
template,
|
||||
});
|
||||
}
|
||||
isHost: boolean;
|
||||
type: CompileTypeMetadata;
|
||||
isComponent: boolean;
|
||||
selector: string;
|
||||
@ -446,9 +381,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
template: CompileTemplateMetadata;
|
||||
|
||||
constructor(
|
||||
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners,
|
||||
hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries,
|
||||
entryComponents, template}: {
|
||||
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
|
||||
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
|
||||
viewQueries, entryComponents, template}: {
|
||||
isHost?: boolean,
|
||||
type?: CompileTypeMetadata,
|
||||
isComponent?: boolean,
|
||||
selector?: string,
|
||||
@ -459,15 +395,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
hostListeners?: {[key: string]: string},
|
||||
hostProperties?: {[key: string]: string},
|
||||
hostAttributes?: {[key: string]: string},
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
viewProviders?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
providers?: CompileProviderMetadata[],
|
||||
viewProviders?: CompileProviderMetadata[],
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
template?: CompileTemplateMetadata,
|
||||
} = {}) {
|
||||
this.isHost = !!isHost;
|
||||
this.type = type;
|
||||
this.isComponent = isComponent;
|
||||
this.selector = selector;
|
||||
@ -487,11 +422,9 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompileDirectiveSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
summaryKind: CompileSummaryKind.Directive,
|
||||
type: this.type,
|
||||
isComponent: this.isComponent,
|
||||
selector: this.selector,
|
||||
@ -514,16 +447,12 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
/**
|
||||
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
|
||||
*/
|
||||
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
||||
CompileDirectiveMetadata {
|
||||
export function createHostComponentMeta(
|
||||
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
|
||||
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||
return CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({
|
||||
reference: Object,
|
||||
name: `${compMeta.type.name}_Host`,
|
||||
moduleUrl: compMeta.type.moduleUrl,
|
||||
isHost: true
|
||||
}),
|
||||
isHost: true,
|
||||
type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
|
||||
template: new CompileTemplateMetadata({
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
template: template,
|
||||
@ -546,14 +475,13 @@ export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
||||
});
|
||||
}
|
||||
|
||||
export interface CompilePipeSummary extends CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
export interface CompilePipeSummary extends CompileTypeSummary {
|
||||
type: CompileTypeMetadata;
|
||||
name: string;
|
||||
pure: boolean;
|
||||
}
|
||||
|
||||
export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||
export class CompilePipeMetadata {
|
||||
type: CompileTypeMetadata;
|
||||
name: string;
|
||||
pure: boolean;
|
||||
@ -567,42 +495,44 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||
this.name = name;
|
||||
this.pure = !!pure;
|
||||
}
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
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 {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
// Note: This should only use interfaces as nested data types
|
||||
// as we need to be able to serialize this from/to JSON!
|
||||
export interface CompileNgModuleSummary extends CompileTypeSummary {
|
||||
type: CompileTypeMetadata;
|
||||
entryComponents: CompileIdentifierMetadata[];
|
||||
providers: CompileProviderMetadata[];
|
||||
importedModules: CompileNgModuleInjectorSummary[];
|
||||
exportedModules: CompileNgModuleInjectorSummary[];
|
||||
}
|
||||
|
||||
export interface CompileNgModuleDirectiveSummary extends CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
type: CompileTypeMetadata;
|
||||
// Note: This is transitive over the exported modules.
|
||||
exportedDirectives: CompileIdentifierMetadata[];
|
||||
// Note: This is transitive over the exported modules.
|
||||
exportedPipes: CompileIdentifierMetadata[];
|
||||
exportedModules: CompileNgModuleDirectiveSummary[];
|
||||
loadingPromises: Promise<any>[];
|
||||
}
|
||||
|
||||
export type CompileNgModuleSummary =
|
||||
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
|
||||
// Note: This is transitive.
|
||||
entryComponents: CompileIdentifierMetadata[];
|
||||
// Note: This is transitive.
|
||||
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
|
||||
// Note: This is transitive.
|
||||
modules: CompileTypeMetadata[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a module.
|
||||
*/
|
||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
export class CompileNgModuleMetadata {
|
||||
type: CompileTypeMetadata;
|
||||
declaredDirectives: CompileIdentifierMetadata[];
|
||||
exportedDirectives: CompileIdentifierMetadata[];
|
||||
declaredPipes: CompileIdentifierMetadata[];
|
||||
|
||||
exportedPipes: CompileIdentifierMetadata[];
|
||||
entryComponents: CompileIdentifierMetadata[];
|
||||
bootstrapComponents: CompileIdentifierMetadata[];
|
||||
@ -620,8 +550,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
|
||||
transitiveModule, id}: {
|
||||
type?: CompileTypeMetadata,
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
providers?: CompileProviderMetadata[],
|
||||
declaredDirectives?: CompileIdentifierMetadata[],
|
||||
exportedDirectives?: CompileIdentifierMetadata[],
|
||||
declaredPipes?: CompileIdentifierMetadata[],
|
||||
@ -649,84 +578,81 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
this.transitiveModule = transitiveModule;
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompileNgModuleSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
summaryKind: CompileSummaryKind.NgModule,
|
||||
type: this.type,
|
||||
entryComponents: this.entryComponents,
|
||||
providers: this.providers,
|
||||
importedModules: this.importedModules,
|
||||
exportedModules: this.exportedModules,
|
||||
exportedDirectives: this.exportedDirectives,
|
||||
exportedPipes: this.exportedPipes,
|
||||
loadingPromises: this.transitiveModule.loadingPromises
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
loadingPromises: this.transitiveModule.loadingPromises
|
||||
entryComponents: this.transitiveModule.entryComponents,
|
||||
providers: this.transitiveModule.providers,
|
||||
modules: this.transitiveModule.modules,
|
||||
exportedDirectives: this.transitiveModule.exportedDirectives,
|
||||
exportedPipes: this.transitiveModule.exportedPipes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class TransitiveCompileNgModuleMetadata {
|
||||
directivesSet = new Set<any>();
|
||||
directives: CompileIdentifierMetadata[] = [];
|
||||
exportedDirectivesSet = new Set<any>();
|
||||
exportedDirectives: CompileIdentifierMetadata[] = [];
|
||||
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(
|
||||
public modules: CompileNgModuleInjectorSummary[], public providers: CompileProviderMetadata[],
|
||||
public entryComponents: CompileIdentifierMetadata[],
|
||||
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
|
||||
public loadingPromises: Promise<any>[]) {
|
||||
directives.forEach(dir => this.directivesSet.add(dir.reference));
|
||||
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
|
||||
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
|
||||
|
||||
addProvider(provider: CompileProviderMetadata, module: CompileIdentifierMetadata) {
|
||||
this.providers.push({provider: provider, module: module});
|
||||
}
|
||||
}
|
||||
|
||||
export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifier>(items: T[]):
|
||||
T[] {
|
||||
const map = new Map<any, T>();
|
||||
|
||||
items.forEach((item) => {
|
||||
if (!map.get(item.identifier.reference)) {
|
||||
map.set(item.identifier.reference, item);
|
||||
addDirective(id: CompileIdentifierMetadata) {
|
||||
if (!this.directivesSet.has(id.reference)) {
|
||||
this.directivesSet.add(id.reference);
|
||||
this.directives.push(id);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(map.values());
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _normalizeArray(obj: any[]): any[] {
|
||||
return obj || [];
|
||||
}
|
||||
|
||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
||||
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
|
||||
}
|
||||
|
||||
export interface StaticSymbol {
|
||||
name: string;
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
export class ProviderMeta {
|
||||
token: any;
|
||||
useClass: Type<any>;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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 * 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...
|
||||
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
|
||||
.toStmt());
|
||||
return new CheckBindingField(fieldExpr, bindingId);
|
||||
}
|
||||
@ -30,7 +30,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
||||
export function createCheckBindingStmt(
|
||||
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||
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
|
||||
]);
|
||||
if (evalResult.forceUpdate) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import * as cdAst from '../expression_parser/ast';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import {ClassBuilder} from '../output/class_builder';
|
||||
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)) {
|
||||
unwrapperStmts.push(
|
||||
VAL_UNWRAPPER_VAR
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
return unwrapperStmts;
|
||||
@ -277,15 +277,20 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||
|
||||
return ast.expressions.length <= 9 ?
|
||||
o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
||||
o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([
|
||||
o.importExpr(createIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
||||
o.importExpr(createIdentifier(Identifiers.interpolate)).callFn([
|
||||
args[0], o.literalArr(args.slice(1))
|
||||
]);
|
||||
}
|
||||
|
||||
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
||||
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||
if (leftMostSafe) {
|
||||
return this.convertSafeAccess(ast, leftMostSafe, mode);
|
||||
} else {
|
||||
return convertToStatementIfNeeded(
|
||||
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
||||
}
|
||||
}
|
||||
|
||||
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
||||
@ -578,7 +583,7 @@ function flattenStatements(arg: any, output: o.Statement[]) {
|
||||
|
||||
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
||||
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 proxyParams: o.FnParam[] = [];
|
||||
@ -599,7 +604,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
|
||||
function createCachedLiteralMap(
|
||||
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
|
||||
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 proxyParams: o.FnParam[] = [];
|
||||
|
@ -8,15 +8,12 @@
|
||||
|
||||
import {CompileTokenMetadata} from '../compile_metadata';
|
||||
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';
|
||||
|
||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||
if (isPresent(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 {
|
||||
return o.importExpr(token.identifier);
|
||||
}
|
||||
@ -24,13 +21,13 @@ export function createDiTokenExpression(token: CompileTokenMetadata): o.Expressi
|
||||
|
||||
export function createInlineArray(values: o.Expression[]): o.Expression {
|
||||
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 index = Math.ceil(log2);
|
||||
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
|
||||
Identifiers.InlineArrayDynamic;
|
||||
const identifier = resolveIdentifier(identifierSpec);
|
||||
const identifier = createIdentifier(identifierSpec);
|
||||
return o.importExpr(identifier).instantiate([
|
||||
<o.Expression>o.literal(values.length)
|
||||
].concat(values));
|
||||
@ -46,7 +43,7 @@ export function createPureProxy(
|
||||
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||
}
|
||||
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
|
||||
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
|
||||
.set(o.importExpr(createIdentifier(pureProxyId)).callFn([fn]))
|
||||
.toStmt());
|
||||
}
|
||||
|
||||
@ -56,5 +53,5 @@ export function createEnumExpression(enumType: IdentifierSpec, enumValue: any):
|
||||
if (!enumName) {
|
||||
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 {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||
@ -26,7 +26,7 @@ export function writeToRenderer(
|
||||
case PropertyBindingType.Property:
|
||||
if (logBindingUpdate) {
|
||||
updateStmts.push(
|
||||
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
|
||||
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
|
||||
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
}
|
||||
@ -104,7 +104,7 @@ export function triggerAnimation(
|
||||
// it's important to normalize the void value as `void` explicitly
|
||||
// so that the styles data can be obtained from the stringmap
|
||||
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);
|
||||
|
||||
updateStmts.push(
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {ViewEncapsulation, isDevMode} from '@angular/core';
|
||||
|
||||
import {CompileIdentifierMetadata} from './compile_metadata';
|
||||
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||
import {Identifiers, createIdentifier} from './identifiers';
|
||||
|
||||
function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
@ -61,7 +61,7 @@ export abstract class RenderTypes {
|
||||
}
|
||||
|
||||
export class DefaultRenderTypes implements RenderTypes {
|
||||
get renderer() { return resolveIdentifier(Identifiers.Renderer); };
|
||||
get renderer() { return createIdentifier(Identifiers.Renderer); };
|
||||
renderText: any = null;
|
||||
renderElement: any = null;
|
||||
renderComment: any = null;
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
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 {ReflectorReader, reflector} from './private_import_core';
|
||||
import {splitAtColon} from './util';
|
||||
|
||||
|
||||
/*
|
||||
* Resolve a `Type` for {@link Directive}.
|
||||
*
|
||||
@ -35,7 +36,7 @@ export class DirectiveResolver {
|
||||
resolve(type: Type<any>, throwIfNotFound = true): Directive {
|
||||
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||
if (typeMetadata) {
|
||||
const metadata = typeMetadata.find(isDirectiveMetadata);
|
||||
const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata);
|
||||
if (metadata) {
|
||||
const propertyMetadata = this._reflector.propMetadata(type);
|
||||
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
||||
@ -58,85 +59,76 @@ export class DirectiveResolver {
|
||||
const queries: {[key: string]: any} = {};
|
||||
|
||||
Object.keys(propertyMetadata).forEach((propName: string) => {
|
||||
|
||||
propertyMetadata[propName].forEach(a => {
|
||||
if (a instanceof Input) {
|
||||
if (a.bindingPropertyName) {
|
||||
inputs.push(`${propName}: ${a.bindingPropertyName}`);
|
||||
} else {
|
||||
inputs.push(propName);
|
||||
}
|
||||
} else if (a instanceof Output) {
|
||||
const output: Output = a;
|
||||
if (output.bindingPropertyName) {
|
||||
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
||||
} else {
|
||||
outputs.push(propName);
|
||||
}
|
||||
} else if (a instanceof HostBinding) {
|
||||
const hostBinding: HostBinding = a;
|
||||
if (hostBinding.hostPropertyName) {
|
||||
const startWith = hostBinding.hostPropertyName[0];
|
||||
if (startWith === '(') {
|
||||
throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
||||
} else if (startWith === '[') {
|
||||
throw new Error(
|
||||
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||
}
|
||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
} else if (a instanceof HostListener) {
|
||||
const hostListener: HostListener = a;
|
||||
const args = hostListener.args || [];
|
||||
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
||||
} else if (a instanceof Query) {
|
||||
queries[propName] = a;
|
||||
const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input);
|
||||
if (input) {
|
||||
if (input.bindingPropertyName) {
|
||||
inputs.push(`${propName}: ${input.bindingPropertyName}`);
|
||||
} else {
|
||||
inputs.push(propName);
|
||||
}
|
||||
});
|
||||
}
|
||||
const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output);
|
||||
if (output) {
|
||||
if (output.bindingPropertyName) {
|
||||
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
||||
} else {
|
||||
outputs.push(propName);
|
||||
}
|
||||
}
|
||||
const hostBinding =
|
||||
ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof HostBinding);
|
||||
if (hostBinding) {
|
||||
if (hostBinding.hostPropertyName) {
|
||||
const startWith = hostBinding.hostPropertyName[0];
|
||||
if (startWith === '(') {
|
||||
throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
||||
} else if (startWith === '[') {
|
||||
throw new Error(
|
||||
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||
}
|
||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
}
|
||||
const hostListener =
|
||||
ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof HostListener);
|
||||
if (hostListener) {
|
||||
const args = hostListener.args || [];
|
||||
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
||||
}
|
||||
const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query);
|
||||
if (query) {
|
||||
queries[propName] = query;
|
||||
}
|
||||
});
|
||||
return this._merge(dm, inputs, outputs, host, queries, directiveType);
|
||||
}
|
||||
|
||||
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(
|
||||
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
||||
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
|
||||
const mergedInputs: string[] = inputs;
|
||||
|
||||
if (directive.inputs) {
|
||||
const inputNames: string[] =
|
||||
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 mergedInputs =
|
||||
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
|
||||
const mergedOutputs =
|
||||
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
|
||||
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
|
||||
const mergedQueries =
|
||||
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
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 {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||
import {CompilerConfig} from './config';
|
||||
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 {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||
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()
|
||||
export class DirectiveWrapperCompiler {
|
||||
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; }
|
||||
static dirWrapperClassName(id: CompileIdentifierMetadata) {
|
||||
return `Wrapper_${identifierName(id)}`;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
||||
@ -117,10 +119,10 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
||||
[
|
||||
new o.FnParam(
|
||||
VIEW_VAR.name,
|
||||
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
new o.FnParam(
|
||||
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),
|
||||
],
|
||||
this.detachStmts),
|
||||
@ -172,7 +174,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
||||
}
|
||||
if (builder.compilerConfig.logBindingUpdate) {
|
||||
onChangesStmts.push(
|
||||
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
||||
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
||||
.callFn(
|
||||
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
|
||||
.toStmt());
|
||||
@ -198,7 +200,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
||||
'ngDoCheck',
|
||||
[
|
||||
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(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||
],
|
||||
@ -214,7 +216,7 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
||||
if (builder.genChanges) {
|
||||
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
||||
.key(o.literal(input))
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
|
||||
.instantiate([field.expression, CURR_VALUE_VAR]))
|
||||
.toStmt());
|
||||
}
|
||||
@ -237,10 +239,10 @@ function addCheckHostMethod(
|
||||
const stmts: o.Statement[] = [];
|
||||
const methodParams: 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(
|
||||
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(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||
];
|
||||
@ -255,14 +257,14 @@ function addCheckHostMethod(
|
||||
if (hostProp.needsRuntimeSecurityContext) {
|
||||
securityContextExpr = o.variable(`secCtx_${methodParams.length}`);
|
||||
methodParams.push(new o.FnParam(
|
||||
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext))));
|
||||
securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
|
||||
}
|
||||
let checkBindingStmts: o.Statement[];
|
||||
if (hostProp.isAnimation) {
|
||||
const {updateStmts, detachStmts} = triggerAnimation(
|
||||
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp,
|
||||
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);
|
||||
checkBindingStmts = updateStmts;
|
||||
builder.detachStmts.push(...detachStmts);
|
||||
@ -306,7 +308,7 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
|
||||
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
|
||||
const methodParams: 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)
|
||||
];
|
||||
const stmts: o.Statement[] = [
|
||||
@ -348,9 +350,10 @@ function parseHostBindings(
|
||||
const errors: ParseError[] = [];
|
||||
const parser =
|
||||
new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors);
|
||||
const sourceFileName = dirMeta.type.moduleUrl ?
|
||||
`in Directive ${dirMeta.type.name} in ${dirMeta.type.moduleUrl}` :
|
||||
`in Directive ${dirMeta.type.name}`;
|
||||
const moduleUrl = identifierModuleUrl(dirMeta.type);
|
||||
const sourceFileName = moduleUrl ?
|
||||
`in Directive ${identifierName(dirMeta.type)} in ${moduleUrl}` :
|
||||
`in Directive ${identifierName(dirMeta.type)}`;
|
||||
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||
const sourceSpan = new ParseSourceSpan(
|
||||
new ParseLocation(sourceFile, null, null, null),
|
||||
|
@ -8,10 +8,16 @@
|
||||
|
||||
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}]`);
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
@ -39,7 +45,7 @@ class _SerializerVisitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -53,6 +59,21 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
|
||||
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
|
||||
*
|
||||
@ -63,7 +84,7 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
|
||||
*/
|
||||
export function sha1(str: string): string {
|
||||
const utf8 = utf8Encode(str);
|
||||
const words32 = stringToWords32(utf8);
|
||||
const words32 = stringToWords32(utf8, Endian.Big);
|
||||
const len = utf8.length * 8;
|
||||
|
||||
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)];
|
||||
}
|
||||
|
||||
const sha1 = words32ToString([a, b, c, d, e]);
|
||||
return byteStringToHexString(words32ToByteString([a, b, c, d, e]));
|
||||
}
|
||||
|
||||
let hex: string = '';
|
||||
for (let i = 0; i < sha1.length; i++) {
|
||||
const b = sha1.charCodeAt(i);
|
||||
hex += (b >>> 4 & 0x0f).toString(16) + (b & 0x0f).toString(16);
|
||||
function fk(index: number, b: number, c: number, d: number): [number, number] {
|
||||
if (index < 20) {
|
||||
return [(b & c) | (~b & d), 0x5a827999];
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -131,10 +236,9 @@ function decodeSurrogatePairs(str: string, index: number): number {
|
||||
}
|
||||
|
||||
const high = str.charCodeAt(index);
|
||||
let low: number;
|
||||
|
||||
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) {
|
||||
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
|
||||
}
|
||||
@ -143,50 +247,126 @@ function decodeSurrogatePairs(str: string, index: number): number {
|
||||
return high;
|
||||
}
|
||||
|
||||
function stringToWords32(str: string): number[] {
|
||||
const words32 = Array(str.length >>> 2);
|
||||
function add32(a: number, b: number): number {
|
||||
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++) {
|
||||
words32[i] = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
words32[i >>> 2] |= (str.charCodeAt(i) & 0xff) << 8 * (3 - i & 0x3);
|
||||
words32[i] = wordAt(str, i * 4, endian);
|
||||
}
|
||||
|
||||
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 = '';
|
||||
for (let i = 0; i < words32.length * 4; i++) {
|
||||
str += String.fromCharCode((words32[i >>> 2] >>> 8 * (3 - i & 0x3)) & 0xff);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
str += String.fromCharCode((word >>> 8 * (3 - i)) & 0xff);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function fk(index: number, b: number, c: number, d: number): [number, number] {
|
||||
if (index < 20) {
|
||||
return [(b & c) | (~b & d), 0x5a827999];
|
||||
function byteStringToHexString(str: string): string {
|
||||
let hex: string = '';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const b = byteAt(str, i);
|
||||
hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
|
||||
}
|
||||
|
||||
if (index < 40) {
|
||||
return [b ^ c ^ d, 0x6ed9eba1];
|
||||
}
|
||||
|
||||
if (index < 60) {
|
||||
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
|
||||
}
|
||||
|
||||
return [b ^ c ^ d, 0xca62c1d6];
|
||||
return hex.toLowerCase();
|
||||
}
|
||||
|
||||
function add32(a: number, b: number): number {
|
||||
const low = (a & 0xffff) + (b & 0xffff);
|
||||
const high = (a >> 16) + (b >> 16) + (low >> 16);
|
||||
return (high << 16) | (low & 0xffff);
|
||||
// based on http://www.danvk.org/hex2dec.html (JS can not handle more than 56b)
|
||||
function byteStringToDecString(str: string): string {
|
||||
let decimal = '';
|
||||
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 {
|
||||
return (a << count) | (a >>> (32 - count));
|
||||
}
|
||||
// x and y decimal, lowest significant digit first
|
||||
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;
|
||||
}
|
||||
|
123
modules/@angular/compiler/src/i18n/extractor.ts
Normal file
123
modules/@angular/compiler/src/i18n/extractor.ts
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Extract i18n messages from source code
|
||||
*/
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
||||
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
||||
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector';
|
||||
import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver';
|
||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveResolver} from '../directive_resolver';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {HtmlParser} from '../ml_parser/html_parser';
|
||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {NgModuleResolver} from '../ng_module_resolver';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {PipeResolver} from '../pipe_resolver';
|
||||
import {Console} from '../private_import_core';
|
||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||
import {createOfflineCompileUrlResolver} from '../url_resolver';
|
||||
|
||||
import {I18NHtmlParser} from './i18n_html_parser';
|
||||
import {MessageBundle} from './message_bundle';
|
||||
|
||||
export interface ExtractorOptions {
|
||||
includeFilePattern?: RegExp;
|
||||
excludeFilePattern?: RegExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
||||
* services and from underlying file systems.
|
||||
*/
|
||||
export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHost {
|
||||
/**
|
||||
* Loads a resource (e.g. html / css)
|
||||
*/
|
||||
loadResource(path: string): Promise<string>;
|
||||
}
|
||||
|
||||
export class Extractor {
|
||||
constructor(
|
||||
private options: ExtractorOptions, public host: ExtractorHost,
|
||||
private staticReflector: StaticReflector, private messageBundle: MessageBundle,
|
||||
private metadataResolver: CompileMetadataResolver) {}
|
||||
|
||||
extract(rootFiles: string[]): Promise<MessageBundle> {
|
||||
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options);
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
|
||||
return Promise
|
||||
.all(ngModules.map(
|
||||
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||
ngModule.type.reference, false)))
|
||||
.then(() => {
|
||||
const errors: ParseError[] = [];
|
||||
|
||||
files.forEach(file => {
|
||||
const compMetas: CompileDirectiveMetadata[] = [];
|
||||
file.directives.forEach(directiveType => {
|
||||
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
||||
if (dirMeta && dirMeta.isComponent) {
|
||||
compMetas.push(dirMeta);
|
||||
}
|
||||
});
|
||||
compMetas.forEach(compMeta => {
|
||||
const html = compMeta.template.template;
|
||||
const interpolationConfig =
|
||||
InterpolationConfig.fromArray(compMeta.template.interpolation);
|
||||
errors.push(
|
||||
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
|
||||
});
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||
}
|
||||
|
||||
return this.messageBundle;
|
||||
});
|
||||
}
|
||||
|
||||
static create(host: ExtractorHost, options: ExtractorOptions):
|
||||
{extractor: Extractor, staticReflector: StaticReflector} {
|
||||
const htmlParser = new I18NHtmlParser(new HtmlParser());
|
||||
|
||||
const urlResolver = createOfflineCompileUrlResolver();
|
||||
const staticReflector = new StaticReflector(host);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
|
||||
const config = new CompilerConfig({
|
||||
genDebugInfo: false,
|
||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||
logBindingUpdate: false,
|
||||
useJit: false
|
||||
});
|
||||
|
||||
const normalizer = new DirectiveNormalizer(
|
||||
{get: (url: string) => host.loadResource(url)}, urlResolver, htmlParser, config);
|
||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options),
|
||||
elementSchemaRegistry, normalizer, staticReflector);
|
||||
|
||||
// TODO(vicb): implicit tags & attributes
|
||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||
|
||||
const extractor = new Extractor(options, host, staticReflector, messageBundle, resolver);
|
||||
return {extractor, staticReflector};
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import * as html from '../ml_parser/ast';
|
||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {ParseTreeResult} from '../ml_parser/parser';
|
||||
|
||||
import {digestMessage} from './digest';
|
||||
import * as i18n from './i18n_ast';
|
||||
import {createI18nMessageFactory} from './i18n_parser';
|
||||
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
|
||||
// message
|
||||
const i18nAttr = _getI18nAttr(el);
|
||||
const isImplicit = this._implicitTags.some((tag: string): boolean => el.name === tag) &&
|
||||
!this._inIcu && !this._isInTranslatableSection;
|
||||
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
||||
!this._isInTranslatableSection;
|
||||
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
||||
this._inImplicitNode = this._inImplicitNode || isImplicit;
|
||||
|
||||
@ -348,14 +347,14 @@ class _Visitor implements html.Visitor {
|
||||
// no-op when called in extraction mode (returns [])
|
||||
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
|
||||
if (message && this._mode === _VisitorMode.Merge) {
|
||||
const id = digestMessage(message);
|
||||
const nodes = this._translations.get(id);
|
||||
const nodes = this._translations.get(message);
|
||||
|
||||
if (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 [];
|
||||
@ -384,19 +383,20 @@ class _Visitor implements html.Visitor {
|
||||
if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) {
|
||||
const meaning = i18nAttributeMeanings[attr.name];
|
||||
const message: i18n.Message = this._createI18nMessage([attr], meaning, '');
|
||||
const id = digestMessage(message);
|
||||
const nodes = this._translations.get(id);
|
||||
const nodes = this._translations.get(message);
|
||||
if (nodes) {
|
||||
if (nodes[0] instanceof html.Text) {
|
||||
const value = (nodes[0] as html.Text).value;
|
||||
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
|
||||
} else {
|
||||
this._reportError(
|
||||
el, `Unexpected translation for attribute "${attr.name}" (id="${id}")`);
|
||||
el,
|
||||
`Unexpected translation for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
|
||||
}
|
||||
} else {
|
||||
this._reportError(
|
||||
el, `Translation unavailable for attribute "${attr.name}" (id="${id}")`);
|
||||
el,
|
||||
`Translation unavailable for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
|
||||
}
|
||||
} else {
|
||||
translatedAttributes.push(attr);
|
||||
|
@ -12,18 +12,20 @@ export class Message {
|
||||
/**
|
||||
* @param nodes message AST
|
||||
* @param placeholders maps placeholder names to static content
|
||||
* @param placeholderToMsgIds maps placeholder names to translatable message IDs (used for ICU
|
||||
* messages)
|
||||
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
|
||||
* @param meaning
|
||||
* @param description
|
||||
*/
|
||||
constructor(
|
||||
public nodes: Node[], public placeholders: {[name: string]: string},
|
||||
public placeholderToMsgIds: {[name: string]: string}, public meaning: string,
|
||||
public nodes: Node[], public placeholders: {[phName: string]: string},
|
||||
public placeholderToMessage: {[phName: string]: Message}, public meaning: 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 {
|
||||
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); }
|
||||
}
|
||||
|
||||
// TODO(vicb): do we really need this node (vs an array) ?
|
||||
export class Container implements Node {
|
||||
constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {}
|
||||
|
||||
@ -38,6 +41,7 @@ export class Container implements Node {
|
||||
}
|
||||
|
||||
export class Icu implements Node {
|
||||
public expressionPlaceholder: string;
|
||||
constructor(
|
||||
public expression: string, public type: string, public cases: {[k: string]: Node},
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
@ -55,13 +59,13 @@ export class TagPlaceholder 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); }
|
||||
}
|
||||
|
||||
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); }
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/in
|
||||
import {ParseTreeResult} from '../ml_parser/parser';
|
||||
|
||||
import {mergeTranslations} from './extractor_merger';
|
||||
import {MessageBundle} from './message_bundle';
|
||||
import {Serializer} from './serializers/serializer';
|
||||
import {Xliff} from './serializers/xliff';
|
||||
import {Xmb} from './serializers/xmb';
|
||||
@ -41,32 +40,29 @@ export class I18NHtmlParser implements HtmlParser {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors.concat(errors));
|
||||
if (parseResult.errors.length) {
|
||||
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors);
|
||||
}
|
||||
|
||||
const serializer = this._createSerializer(interpolationConfig);
|
||||
const translationBundle =
|
||||
TranslationBundle.load(this._translations, url, messageBundle, serializer);
|
||||
const serializer = this._createSerializer();
|
||||
const translationBundle = TranslationBundle.load(this._translations, url, serializer);
|
||||
|
||||
return mergeTranslations(parseResult.rootNodes, translationBundle, interpolationConfig, [], {});
|
||||
}
|
||||
|
||||
private _createSerializer(interpolationConfig: InterpolationConfig): Serializer {
|
||||
private _createSerializer(): Serializer {
|
||||
const format = (this._translationsFormat || 'xlf').toLowerCase();
|
||||
|
||||
switch (format) {
|
||||
case 'xmb':
|
||||
return new Xmb();
|
||||
case 'xtb':
|
||||
return new Xtb(this._htmlParser, interpolationConfig);
|
||||
return new Xtb();
|
||||
case 'xliff':
|
||||
case 'xlf':
|
||||
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 {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {ParseSourceSpan} from '../parse_util';
|
||||
import {digestMessage} from './digest';
|
||||
|
||||
import * as i18n from './i18n_ast';
|
||||
import {PlaceholderRegistry} from './serializers/placeholder';
|
||||
@ -34,8 +33,8 @@ class _I18nVisitor implements html.Visitor {
|
||||
private _isIcu: boolean;
|
||||
private _icuDepth: number;
|
||||
private _placeholderRegistry: PlaceholderRegistry;
|
||||
private _placeholderToContent: {[name: string]: string};
|
||||
private _placeholderToIds: {[name: string]: string};
|
||||
private _placeholderToContent: {[phName: string]: string};
|
||||
private _placeholderToMessage: {[phName: string]: i18n.Message};
|
||||
|
||||
constructor(
|
||||
private _expressionParser: ExpressionParser,
|
||||
@ -46,12 +45,12 @@ class _I18nVisitor implements html.Visitor {
|
||||
this._icuDepth = 0;
|
||||
this._placeholderRegistry = new PlaceholderRegistry();
|
||||
this._placeholderToContent = {};
|
||||
this._placeholderToIds = {};
|
||||
this._placeholderToMessage = {};
|
||||
|
||||
const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {});
|
||||
|
||||
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 {
|
||||
@ -99,7 +98,13 @@ class _I18nVisitor implements html.Visitor {
|
||||
this._icuDepth--;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export {Extractor, ExtractorHost, ExtractorOptions} from './extractor';
|
||||
export {I18NHtmlParser} from './i18n_html_parser';
|
||||
export {MessageBundle} from './message_bundle';
|
||||
export {Serializer} from './serializers/serializer';
|
||||
|
@ -10,7 +10,6 @@ import {HtmlParser} from '../ml_parser/html_parser';
|
||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {ParseError} from '../parse_util';
|
||||
|
||||
import {digestMessage} from './digest';
|
||||
import {extractMessages} from './extractor_merger';
|
||||
import {Message} from './i18n_ast';
|
||||
import {Serializer} from './serializers/serializer';
|
||||
@ -19,7 +18,7 @@ import {Serializer} from './serializers/serializer';
|
||||
* A container for message extracted from the templates.
|
||||
*/
|
||||
export class MessageBundle {
|
||||
private _messageMap: {[id: string]: Message} = {};
|
||||
private _messages: Message[] = [];
|
||||
|
||||
constructor(
|
||||
private _htmlParser: HtmlParser, private _implicitTags: string[],
|
||||
@ -40,11 +39,10 @@ export class MessageBundle {
|
||||
return i18nParserResult.errors;
|
||||
}
|
||||
|
||||
i18nParserResult.messages.forEach(
|
||||
(message) => { this._messageMap[digestMessage(message)] = message; });
|
||||
this._messages.push(...i18nParserResult.messages);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
@ -93,6 +95,10 @@ export class PlaceholderRegistry {
|
||||
return uniqueName;
|
||||
}
|
||||
|
||||
getUniquePlaceholder(name: string): string {
|
||||
return this._generateUniqueName(name.toUpperCase());
|
||||
}
|
||||
|
||||
// Generate a hash for a tag - does not take attribute order into account
|
||||
private _hashTag(tag: string, attrs: {[k: string]: string}, isVoid: boolean): string {
|
||||
const start = `<${tag}`;
|
||||
@ -105,18 +111,8 @@ export class PlaceholderRegistry {
|
||||
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
||||
|
||||
private _generateUniqueName(base: string): string {
|
||||
let name = base;
|
||||
let next = this._placeHolderNameCounts[name];
|
||||
|
||||
if (!next) {
|
||||
next = 1;
|
||||
} else {
|
||||
name += `_${next}`;
|
||||
next++;
|
||||
}
|
||||
|
||||
this._placeHolderNameCounts[base] = next;
|
||||
|
||||
return name;
|
||||
const next = this._placeHolderNameCounts[base];
|
||||
this._placeHolderNameCounts[base] = next ? next + 1 : 1;
|
||||
return next ? `${base}_${next}` : base;
|
||||
}
|
||||
}
|
||||
|
@ -6,36 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as html from '../../ml_parser/ast';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
|
||||
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[]};
|
||||
|
||||
// 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;
|
||||
digest(message: i18n.Message): string;
|
||||
}
|
@ -6,17 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ListWrapper} from '../../facade/collection';
|
||||
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 {ParseError} from '../../parse_util';
|
||||
import {digest} from '../digest';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
import {I18nError} from '../parse_util';
|
||||
|
||||
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer';
|
||||
import {Serializer} from './serializer';
|
||||
import * as xml from './xml_helper';
|
||||
|
||||
const _VERSION = '1.2';
|
||||
@ -24,6 +20,7 @@ const _XMLNS = 'urn:oasis:names:tc:xliff:document:1.2';
|
||||
// TODO(vicb): make this a param (s/_/-/)
|
||||
const _SOURCE_LANG = 'en';
|
||||
const _PLACEHOLDER_TAG = 'x';
|
||||
|
||||
const _SOURCE_TAG = 'source';
|
||||
const _TARGET_TAG = 'target';
|
||||
const _UNIT_TAG = 'trans-unit';
|
||||
@ -31,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/xliff-profile-html/xliff-profile-html-1.2.html
|
||||
export class Xliff implements Serializer {
|
||||
constructor(private _htmlParser: HtmlParser, private _interpolationConfig: InterpolationConfig) {}
|
||||
|
||||
write(messageMap: {[id: string]: i18n.Message}): string {
|
||||
write(messages: i18n.Message[]): string {
|
||||
const visitor = new _WriteVisitor();
|
||||
|
||||
const visited: {[id: string]: boolean} = {};
|
||||
const transUnits: xml.Node[] = [];
|
||||
|
||||
Object.keys(messageMap).forEach((id) => {
|
||||
const message = messageMap[id];
|
||||
messages.forEach(message => {
|
||||
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(
|
||||
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
||||
new xml.CR(8), new xml.Tag(_TARGET_TAG));
|
||||
@ -76,38 +75,28 @@ export class Xliff implements Serializer {
|
||||
]);
|
||||
}
|
||||
|
||||
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} {
|
||||
// Parse the xtb file into xml nodes
|
||||
const result = new XmlParser().parse(content, url);
|
||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||
// xliff to xml nodes
|
||||
const xliffParser = new XliffParser();
|
||||
const {mlNodesByMsgId, errors} = xliffParser.parse(content, url);
|
||||
|
||||
if (result.errors.length) {
|
||||
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`);
|
||||
}
|
||||
|
||||
// Replace the placeholders, messages are now string
|
||||
const {messages, errors} = new _LoadVisitor().parse(result.rootNodes, messageBundle);
|
||||
|
||||
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;
|
||||
// xml nodes to i18n nodes
|
||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||
const converter = new XmlToI18n();
|
||||
Object.keys(mlNodesByMsgId).forEach(msgId => {
|
||||
const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
|
||||
errors.push(...e);
|
||||
i18nNodesByMsgId[msgId] = i18nNodes;
|
||||
});
|
||||
|
||||
if (parseErrors.length) {
|
||||
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
|
||||
if (errors.length) {
|
||||
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 {
|
||||
@ -162,80 +151,51 @@ class _WriteVisitor implements i18n.Visitor {
|
||||
|
||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||
this._isInIcu = false;
|
||||
return ListWrapper.flatten(nodes.map(node => node.visit(this)));
|
||||
return [].concat(...nodes.map(node => node.visit(this)));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(vicb): add error management (structure)
|
||||
// TODO(vicb): factorize (xtb) ?
|
||||
class _LoadVisitor implements ml.Visitor {
|
||||
private _messageNodes: [string, ml.Node[]][];
|
||||
private _translatedMessages: {[id: string]: string};
|
||||
private _msgId: string;
|
||||
private _target: ml.Node[];
|
||||
// Extract messages as xml nodes from the xliff file
|
||||
class XliffParser implements ml.Visitor {
|
||||
private _unitMlNodes: ml.Node[];
|
||||
private _errors: I18nError[];
|
||||
private _placeholders: {[name: string]: string};
|
||||
private _placeholderToIds: {[name: string]: string};
|
||||
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||
|
||||
parse(nodes: ml.Node[], messageBundle: MessageBundle):
|
||||
{messages: {[k: string]: string}, errors: I18nError[]} {
|
||||
this._messageNodes = [];
|
||||
this._translatedMessages = {};
|
||||
this._msgId = '';
|
||||
this._target = [];
|
||||
this._errors = [];
|
||||
parse(xliff: string, url: string) {
|
||||
this._unitMlNodes = [];
|
||||
this._mlNodesByMsgId = {};
|
||||
|
||||
// Find all messages
|
||||
ml.visitAll(this, nodes, null);
|
||||
const xml = new XmlParser().parse(xliff, url, false);
|
||||
|
||||
const messageMap = messageBundle.getMessageMap();
|
||||
const placeholders = extractPlaceholders(messageBundle);
|
||||
const placeholderToIds = extractPlaceholderToIds(messageBundle);
|
||||
this._errors = xml.errors;
|
||||
ml.visitAll(this, xml.rootNodes, null);
|
||||
|
||||
this._messageNodes
|
||||
.filter(message => {
|
||||
// Remove any messages that is not present in the source message bundle.
|
||||
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};
|
||||
return {
|
||||
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||
errors: this._errors,
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(element: ml.Element, context: any): any {
|
||||
switch (element.name) {
|
||||
case _UNIT_TAG:
|
||||
this._target = null;
|
||||
const msgId = element.attrs.find((attr) => attr.name === 'id');
|
||||
if (!msgId) {
|
||||
this._unitMlNodes = null;
|
||||
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
||||
if (!idAttr) {
|
||||
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
this._msgId = msgId.value;
|
||||
}
|
||||
ml.visitAll(this, element.children, null);
|
||||
if (this._msgId !== null) {
|
||||
this._messageNodes.push([this._msgId, this._target]);
|
||||
const id = idAttr.value;
|
||||
if (this._mlNodesByMsgId.hasOwnProperty(id)) {
|
||||
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||
} else {
|
||||
ml.visitAll(this, element.children, null);
|
||||
if (this._unitMlNodes) {
|
||||
this._mlNodesByMsgId[id] = this._unitMlNodes;
|
||||
} else {
|
||||
this._addError(element, `Message ${id} misses a translation`);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -244,48 +204,65 @@ class _LoadVisitor implements ml.Visitor {
|
||||
break;
|
||||
|
||||
case _TARGET_TAG:
|
||||
this._target = 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`);
|
||||
}
|
||||
this._unitMlNodes = element.children;
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO(vicb): assert file structure, xliff version
|
||||
// For now only recurse on unhandled nodes
|
||||
ml.visitAll(this, element.children, null);
|
||||
}
|
||||
}
|
||||
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {}
|
||||
|
||||
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) {
|
||||
const nameAttr = el.attrs.find((attr) => attr.name === 'id');
|
||||
if (nameAttr) {
|
||||
return new i18n.Placeholder('', nameAttr.value, el.sourceSpan);
|
||||
}
|
||||
|
||||
visitExpansion(expansion: ml.Expansion, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
this._addError(el, `Unexpected tag`);
|
||||
}
|
||||
}
|
||||
|
||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
}
|
||||
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 {
|
||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||
|
@ -6,10 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ListWrapper} from '../../facade/collection';
|
||||
import * as html from '../../ml_parser/ast';
|
||||
import {decimalDigest} from '../digest';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
|
||||
import {Serializer} from './serializer';
|
||||
import * as xml from './xml_helper';
|
||||
@ -40,12 +38,18 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
||||
<!ELEMENT ex (#PCDATA)>`;
|
||||
|
||||
export class Xmb implements Serializer {
|
||||
write(messageMap: {[k: string]: i18n.Message}): string {
|
||||
write(messages: i18n.Message[]): string {
|
||||
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};
|
||||
|
||||
if (message.description) {
|
||||
@ -72,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');
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return digest(message); }
|
||||
}
|
||||
|
||||
class _Visitor implements i18n.Visitor {
|
||||
@ -87,7 +93,7 @@ class _Visitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
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) => {
|
||||
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
|
||||
@ -121,6 +127,10 @@ class _Visitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||
return ListWrapper.flatten(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 {HtmlParser} from '../../ml_parser/html_parser';
|
||||
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
||||
import {XmlParser} from '../../ml_parser/xml_parser';
|
||||
import {ParseError} from '../../parse_util';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
import {I18nError} from '../parse_util';
|
||||
|
||||
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer';
|
||||
import {Serializer} from './serializer';
|
||||
import {digest} from './xmb';
|
||||
|
||||
const _TRANSLATIONS_TAG = 'translationbundle';
|
||||
const _TRANSLATION_TAG = 'translation';
|
||||
const _PLACEHOLDER_TAG = 'ph';
|
||||
|
||||
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[]} {
|
||||
// Parse the xtb file into xml nodes
|
||||
const result = new XmlParser().parse(content, url);
|
||||
|
||||
if (result.errors.length) {
|
||||
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`);
|
||||
}
|
||||
|
||||
// Replace the placeholders, messages are now string
|
||||
const {messages, errors} = new _Visitor().parse(result.rootNodes, messageBundle);
|
||||
// xml nodes to i18n nodes
|
||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||
const converter = new XmlToI18n();
|
||||
Object.keys(mlNodesByMsgId).forEach(msgId => {
|
||||
const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
|
||||
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) {
|
||||
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
|
||||
}
|
||||
|
||||
return messageMap;
|
||||
return i18nNodesByMsgId;
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return digest(message); }
|
||||
}
|
||||
|
||||
class _Visitor implements ml.Visitor {
|
||||
private _messageNodes: [string, ml.Node[]][];
|
||||
private _translatedMessages: {[id: string]: string};
|
||||
// Extract messages as xml nodes from the xtb file
|
||||
class XtbParser implements ml.Visitor {
|
||||
private _bundleDepth: number;
|
||||
private _translationDepth: number;
|
||||
private _errors: I18nError[];
|
||||
private _placeholders: {[name: string]: string};
|
||||
private _placeholderToIds: {[name: string]: string};
|
||||
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||
|
||||
parse(nodes: ml.Node[], messageBundle: MessageBundle):
|
||||
{messages: {[k: string]: string}, errors: I18nError[]} {
|
||||
this._messageNodes = [];
|
||||
this._translatedMessages = {};
|
||||
parse(xtb: string, url: string) {
|
||||
this._bundleDepth = 0;
|
||||
this._translationDepth = 0;
|
||||
this._errors = [];
|
||||
this._mlNodesByMsgId = {};
|
||||
|
||||
// Find all messages
|
||||
ml.visitAll(this, nodes, null);
|
||||
const xml = new XmlParser().parse(xtb, url, true);
|
||||
|
||||
const messageMap = messageBundle.getMessageMap();
|
||||
const placeholders = extractPlaceholders(messageBundle);
|
||||
const placeholderToIds = extractPlaceholderToIds(messageBundle);
|
||||
this._errors = xml.errors;
|
||||
ml.visitAll(this, xml.rootNodes);
|
||||
|
||||
this._messageNodes
|
||||
.filter(message => {
|
||||
// Remove any messages that is not present in the source message bundle.
|
||||
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};
|
||||
return {
|
||||
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||
errors: this._errors,
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(element: ml.Element, context: any): any {
|
||||
@ -127,40 +78,16 @@ class _Visitor implements ml.Visitor {
|
||||
break;
|
||||
|
||||
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');
|
||||
if (!idAttr) {
|
||||
this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
// ICU placeholders are reference to other messages.
|
||||
// The referenced message might not have been decoded yet.
|
||||
// We need to have all messages available to make sure deps are decoded first.
|
||||
// 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 {
|
||||
const name = nameAttr.value;
|
||||
if (this._placeholders.hasOwnProperty(name)) {
|
||||
return this._placeholders[name];
|
||||
const id = idAttr.value;
|
||||
if (this._mlNodesByMsgId.hasOwnProperty(id)) {
|
||||
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||
} else {
|
||||
this._mlNodesByMsgId[id] = element.children;
|
||||
}
|
||||
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;
|
||||
|
||||
@ -169,23 +96,68 @@ class _Visitor implements ml.Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
}
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {}
|
||||
|
||||
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 {
|
||||
const strCases = expansion.cases.map(c => c.visit(this, null));
|
||||
visitExpansion(expansion: ml.Expansion, context: any): any {}
|
||||
|
||||
return `{${expansion.switchValue}, ${expansion.type}, strCases.join(' ')}`;
|
||||
}
|
||||
|
||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {
|
||||
return `${expansionCase.value} {${ml.visitAll(this, expansionCase.expression, null)}}`;
|
||||
}
|
||||
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 (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 {
|
||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||
|
@ -7,22 +7,120 @@
|
||||
*/
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* A container for translated messages
|
||||
*/
|
||||
export class TranslationBundle {
|
||||
constructor(private _messageMap: {[id: string]: html.Node[]} = {}) {}
|
||||
private _i18nToHtml: I18nToHtmlVisitor;
|
||||
|
||||
static load(content: string, url: string, messageBundle: MessageBundle, serializer: Serializer):
|
||||
TranslationBundle {
|
||||
return new TranslationBundle(serializer.load(content, url, messageBundle));
|
||||
constructor(
|
||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||
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,7 +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 {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
import {StaticSymbol} from './aot/static_symbol';
|
||||
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';
|
||||
|
||||
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
||||
@ -339,32 +340,32 @@ export class Identifiers {
|
||||
|
||||
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
|
||||
if (path == null) {
|
||||
return `asset:@angular/lib/${pkg}/index`;
|
||||
return `@angular/${pkg}/index`;
|
||||
} else {
|
||||
return `asset:@angular/lib/${pkg}/src/${path}`;
|
||||
return `@angular/${pkg}/${type}/${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveIdentifier(identifier: IdentifierSpec) {
|
||||
return new CompileIdentifierMetadata({
|
||||
name: identifier.name,
|
||||
moduleUrl: identifier.moduleUrl,
|
||||
reference:
|
||||
reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime)
|
||||
});
|
||||
return reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
||||
}
|
||||
|
||||
export function createIdentifier(identifier: IdentifierSpec): CompileIdentifierMetadata {
|
||||
const reference =
|
||||
reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
||||
return {reference: reference};
|
||||
}
|
||||
|
||||
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
|
||||
return new CompileTokenMetadata({identifier: identifier});
|
||||
return {identifier: identifier};
|
||||
}
|
||||
|
||||
export function resolveIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
|
||||
return identifierToken(resolveIdentifier(identifier));
|
||||
export function createIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
|
||||
return identifierToken(createIdentifier(identifier));
|
||||
}
|
||||
|
||||
export function resolveEnumIdentifier(
|
||||
enumType: CompileIdentifierMetadata, name: string): CompileIdentifierMetadata {
|
||||
const resolvedEnum = reflector.resolveEnum(enumType.reference, name);
|
||||
return new CompileIdentifierMetadata(
|
||||
{name: `${enumType.name}.${name}`, moduleUrl: enumType.moduleUrl, reference: resolvedEnum});
|
||||
export function createEnumIdentifier(
|
||||
enumType: IdentifierSpec, name: string): CompileIdentifierMetadata {
|
||||
const resolvedEnum = reflector.resolveEnum(resolveIdentifier(enumType), name);
|
||||
return {reference: resolvedEnum};
|
||||
}
|
||||
|
@ -8,22 +8,22 @@
|
||||
|
||||
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
||||
|
||||
import {AnimationCompiler} from './animation/animation_compiler';
|
||||
import {AnimationParser} from './animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
|
||||
import {CompilerConfig} from './config';
|
||||
import {DirectiveNormalizer} from './directive_normalizer';
|
||||
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
|
||||
import {stringify} from './facade/lang';
|
||||
import {CompileMetadataResolver} from './metadata_resolver';
|
||||
import {NgModuleCompiler} from './ng_module_compiler';
|
||||
import * as ir from './output/output_ast';
|
||||
import {interpretStatements} from './output/output_interpreter';
|
||||
import {jitStatements} from './output/output_jit';
|
||||
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
|
||||
import {TemplateParser} from './template_parser/template_parser';
|
||||
import {SyncAsyncResult} from './util';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from './view_compiler/view_compiler';
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
import {AnimationParser} from '../animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {stringify} from '../facade/lang';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import * as ir from '../output/output_ast';
|
||||
import {interpretStatements} from '../output/output_interpreter';
|
||||
import {jitStatements} from '../output/output_jit';
|
||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||
import {TemplateParser} from '../template_parser/template_parser';
|
||||
import {SyncAsyncResult} from '../util';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from '../view_compiler/view_compiler';
|
||||
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDepende
|
||||
* application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security).
|
||||
*/
|
||||
@Injectable()
|
||||
export class RuntimeCompiler implements Compiler {
|
||||
export class JitCompiler implements Compiler {
|
||||
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||
private _compiledDirectiveWrapperCache = new Map<Type<any>, Type<any>>();
|
||||
@ -101,16 +101,14 @@ export class RuntimeCompiler implements Compiler {
|
||||
|
||||
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
||||
const loadingPromises: Promise<any>[] = [];
|
||||
const {ngModule, loading} = this._metadataResolver.loadNgModuleMetadata(mainModule, isSync);
|
||||
loadingPromises.push(loading);
|
||||
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
||||
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
||||
// of imported modules.
|
||||
// However, for runtime compilation, we want to transitively compile all modules,
|
||||
// so we also need to call loadNgModuleMetadata for all nested modules.
|
||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||
loadingPromises.push(
|
||||
this._metadataResolver.loadNgModuleMetadata(localModuleMeta.type.reference, isSync)
|
||||
.loading);
|
||||
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||
localModuleMeta.reference, isSync));
|
||||
});
|
||||
return Promise.all(loadingPromises);
|
||||
}
|
||||
@ -126,14 +124,13 @@ export class RuntimeCompiler implements Compiler {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
dep.placeholder.reference =
|
||||
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
|
||||
dep.placeholder.name = `compFactory_${dep.comp.name}`;
|
||||
});
|
||||
if (!this._compilerConfig.useJit) {
|
||||
ngModuleFactory =
|
||||
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
||||
} else {
|
||||
ngModuleFactory = jitStatements(
|
||||
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements,
|
||||
`/${identifierName(moduleMeta.type)}/module.ngfactory.js`, compileResult.statements,
|
||||
compileResult.ngModuleFactoryVar);
|
||||
}
|
||||
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
|
||||
@ -151,7 +148,7 @@ export class RuntimeCompiler implements Compiler {
|
||||
|
||||
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
||||
const localModuleMeta =
|
||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.type.reference);
|
||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference);
|
||||
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||
moduleByDirective.set(dirIdentifier.reference, localModuleMeta);
|
||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||
@ -169,7 +166,7 @@ export class RuntimeCompiler implements Compiler {
|
||||
});
|
||||
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
||||
const localModuleMeta =
|
||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.type.reference);
|
||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference);
|
||||
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||
if (dirMeta.isComponent) {
|
||||
@ -215,7 +212,12 @@ export class RuntimeCompiler implements Compiler {
|
||||
if (!compiledTemplate) {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||
assertComponent(compMeta);
|
||||
const hostMeta = createHostComponentMeta(compMeta);
|
||||
|
||||
class HostClass {
|
||||
static overriddenName = `${identifierName(compMeta.type)}_Host`;
|
||||
}
|
||||
|
||||
const hostMeta = createHostComponentMeta(HostClass, compMeta);
|
||||
compiledTemplate = new CompiledTemplate(
|
||||
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||
@ -264,8 +266,8 @@ export class RuntimeCompiler implements Compiler {
|
||||
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
|
||||
} else {
|
||||
directiveWrapperClass = jitStatements(
|
||||
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements,
|
||||
compileResult.dirWrapperClassVar);
|
||||
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
|
||||
statements, compileResult.dirWrapperClassVar);
|
||||
}
|
||||
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
||||
}
|
||||
@ -288,9 +290,9 @@ export class RuntimeCompiler implements Compiler {
|
||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
|
||||
compMeta.type.name);
|
||||
identifierName(compMeta.type));
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const compileResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||
pipes, compiledAnimations);
|
||||
@ -300,12 +302,10 @@ export class RuntimeCompiler implements Compiler {
|
||||
const vfd = <ViewClassDependency>dep;
|
||||
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
|
||||
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
||||
vfd.placeholder.name = `View_${vfd.comp.name}`;
|
||||
} else if (dep instanceof ComponentFactoryDependency) {
|
||||
const cfd = <ComponentFactoryDependency>dep;
|
||||
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
|
||||
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
||||
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||
const dwd = <DirectiveWrapperDependency>dep;
|
||||
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
|
||||
@ -319,7 +319,7 @@ export class RuntimeCompiler implements Compiler {
|
||||
viewClass = interpretStatements(statements, compileResult.viewClassVar);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
template.compiled(viewClass);
|
||||
@ -332,7 +332,6 @@ export class RuntimeCompiler implements Compiler {
|
||||
const nestedStylesArr = this._resolveAndEvalStylesCompileResult(
|
||||
nestedCompileResult, externalStylesheetsByModuleUrl);
|
||||
dep.valuePlaceholder.reference = nestedStylesArr;
|
||||
dep.valuePlaceholder.name = `importedStyles${i}`;
|
||||
});
|
||||
}
|
||||
|
||||
@ -380,15 +379,16 @@ class CompiledTemplate {
|
||||
|
||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||
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.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements `Compiler` by delegating to the RuntimeCompiler using a known module.
|
||||
* Implements `Compiler` by delegating to the JitCompiler using a known module.
|
||||
*/
|
||||
class ModuleBoundCompiler implements Compiler {
|
||||
constructor(private _delegate: RuntimeCompiler, private _ngModule: Type<any>) {}
|
||||
constructor(private _delegate: JitCompiler, private _ngModule: Type<any>) {}
|
||||
|
||||
get _injector(): Injector { return this._delegate.injector; }
|
||||
|
@ -8,28 +8,30 @@
|
||||
|
||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||
|
||||
import {AnimationParser} from './animation/animation_parser';
|
||||
import {CompilerConfig} from './config';
|
||||
import {DirectiveNormalizer} from './directive_normalizer';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
|
||||
import {Lexer} from './expression_parser/lexer';
|
||||
import {Parser} from './expression_parser/parser';
|
||||
import * as i18n from './i18n/index';
|
||||
import {CompileMetadataResolver} from './metadata_resolver';
|
||||
import {HtmlParser} from './ml_parser/html_parser';
|
||||
import {NgModuleCompiler} from './ng_module_compiler';
|
||||
import {NgModuleResolver} from './ng_module_resolver';
|
||||
import {PipeResolver} from './pipe_resolver';
|
||||
import {Console, ReflectionCapabilities, Reflector, ReflectorReader, reflector} from './private_import_core';
|
||||
import {ResourceLoader} from './resource_loader';
|
||||
import {RuntimeCompiler} from './runtime_compiler';
|
||||
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
|
||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||
import {StyleCompiler} from './style_compiler';
|
||||
import {TemplateParser} from './template_parser/template_parser';
|
||||
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from './url_resolver';
|
||||
import {ViewCompiler} from './view_compiler/view_compiler';
|
||||
import {AnimationParser} from '../animation/animation_parser';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveResolver} from '../directive_resolver';
|
||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {Lexer} from '../expression_parser/lexer';
|
||||
import {Parser} from '../expression_parser/parser';
|
||||
import * as i18n from '../i18n/index';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {HtmlParser} from '../ml_parser/html_parser';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {NgModuleResolver} from '../ng_module_resolver';
|
||||
import {PipeResolver} from '../pipe_resolver';
|
||||
import {Console, ReflectionCapabilities, Reflector, ReflectorReader, reflector} from '../private_import_core';
|
||||
import {ResourceLoader} from '../resource_loader';
|
||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
import {StyleCompiler} from '../style_compiler';
|
||||
import {SummaryResolver} from '../summary_resolver';
|
||||
import {TemplateParser} from '../template_parser/template_parser';
|
||||
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
|
||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||
|
||||
import {JitCompiler} from './compiler';
|
||||
|
||||
const _NO_RESOURCE_LOADER: ResourceLoader = {
|
||||
get(url: string): Promise<string>{
|
||||
@ -38,13 +40,14 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A set of providers that provide `RuntimeCompiler` and its dependencies to use for
|
||||
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
||||
* template compilation.
|
||||
*/
|
||||
export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> = [
|
||||
{provide: Reflector, useValue: reflector},
|
||||
{provide: ReflectorReader, useExisting: Reflector},
|
||||
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
|
||||
SummaryResolver,
|
||||
Console,
|
||||
Lexer,
|
||||
Parser,
|
||||
@ -68,8 +71,8 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||
NgModuleCompiler,
|
||||
DirectiveWrapperCompiler,
|
||||
{provide: CompilerConfig, useValue: new CompilerConfig()},
|
||||
RuntimeCompiler,
|
||||
{provide: Compiler, useExisting: RuntimeCompiler},
|
||||
JitCompiler,
|
||||
{provide: Compiler, useExisting: JitCompiler},
|
||||
DomElementSchemaRegistry,
|
||||
{provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
|
||||
UrlResolver,
|
||||
@ -81,7 +84,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class RuntimeCompilerFactory implements CompilerFactory {
|
||||
export class JitCompilerFactory implements CompilerFactory {
|
||||
private _defaultOptions: CompilerOptions[];
|
||||
constructor(@Inject(COMPILER_OPTIONS) defaultOptions: CompilerOptions[]) {
|
||||
this._defaultOptions = [<CompilerOptions>{
|
||||
@ -128,7 +131,7 @@ function _initReflector() {
|
||||
*/
|
||||
export const platformCoreDynamic = createPlatformFactory(platformCore, 'coreDynamic', [
|
||||
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
|
||||
{provide: CompilerFactory, useClass: RuntimeCompilerFactory},
|
||||
{provide: CompilerFactory, useClass: JitCompilerFactory},
|
||||
{provide: PLATFORM_INITIALIZER, useValue: _initReflector, multi: true},
|
||||
]);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,10 +8,10 @@
|
||||
|
||||
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 {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 * as o from './output/output_ast';
|
||||
import {convertValueToOutputAst} from './output/value_util';
|
||||
@ -35,9 +35,10 @@ export class NgModuleCompileResult {
|
||||
export class NgModuleCompiler {
|
||||
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
||||
NgModuleCompileResult {
|
||||
const sourceFileName = isPresent(ngModuleMeta.type.moduleUrl) ?
|
||||
`in NgModule ${ngModuleMeta.type.name} in ${ngModuleMeta.type.moduleUrl}` :
|
||||
`in NgModule ${ngModuleMeta.type.name}`;
|
||||
const moduleUrl = identifierModuleUrl(ngModuleMeta.type);
|
||||
const sourceFileName = isPresent(moduleUrl) ?
|
||||
`in NgModule ${identifierName(ngModuleMeta.type)} in ${moduleUrl}` :
|
||||
`in NgModule ${identifierName(ngModuleMeta.type)}`;
|
||||
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||
const sourceSpan = new ParseSourceSpan(
|
||||
new ParseLocation(sourceFile, null, null, null),
|
||||
@ -46,8 +47,9 @@ export class NgModuleCompiler {
|
||||
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
|
||||
const entryComponentFactories =
|
||||
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
|
||||
const id = new CompileIdentifierMetadata({name: entryComponent.name});
|
||||
if (ngModuleMeta.bootstrapComponents.indexOf(entryComponent) > -1) {
|
||||
const id: CompileIdentifierMetadata = {reference: null};
|
||||
if (ngModuleMeta.bootstrapComponents.some(
|
||||
(id) => id.reference === entryComponent.reference)) {
|
||||
bootstrapComponentFactories.push(id);
|
||||
}
|
||||
deps.push(new ComponentFactoryDependency(entryComponent, id));
|
||||
@ -59,21 +61,21 @@ export class NgModuleCompiler {
|
||||
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
||||
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
||||
const injectorClass = builder.build();
|
||||
const ngModuleFactoryVar = `${ngModuleMeta.type.name}NgFactory`;
|
||||
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
||||
const ngModuleFactoryStmt =
|
||||
o.variable(ngModuleFactoryVar)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.NgModuleFactory))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.NgModuleFactory))
|
||||
.instantiate(
|
||||
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
|
||||
o.importType(
|
||||
resolveIdentifier(Identifiers.NgModuleFactory),
|
||||
createIdentifier(Identifiers.NgModuleFactory),
|
||||
[o.importType(ngModuleMeta.type)], [o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||
|
||||
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
||||
if (ngModuleMeta.id) {
|
||||
const registerFactoryStmt =
|
||||
o.importExpr(resolveIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||
o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
|
||||
.toStmt();
|
||||
stmts.push(registerFactoryStmt);
|
||||
@ -102,7 +104,7 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
addProvider(resolvedProvider: ProviderAst) {
|
||||
const providerValueExpressions =
|
||||
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(
|
||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||
resolvedProvider.eager);
|
||||
@ -110,12 +112,12 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt());
|
||||
}
|
||||
this._tokens.push(resolvedProvider.token);
|
||||
this._instances.set(resolvedProvider.token.reference, instance);
|
||||
this._instances.set(tokenReference(resolvedProvider.token), instance);
|
||||
}
|
||||
|
||||
build(): o.ClassStmt {
|
||||
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(
|
||||
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
||||
[new o.ReturnStatement(providerExpr)]);
|
||||
@ -143,13 +145,13 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
o.literalArr(this._bootstrapComponentFactories.map(
|
||||
(componentFactory) => o.importExpr(componentFactory)))
|
||||
];
|
||||
const injClassName = `${this._ngModuleMeta.type.name}Injector`;
|
||||
const injClassName = `${identifierName(this._ngModuleMeta.type)}Injector`;
|
||||
return createClassStmt({
|
||||
name: injClassName,
|
||||
ctorParams: [new o.FnParam(
|
||||
InjectorProps.parent.name, o.importType(resolveIdentifier(Identifiers.Injector)))],
|
||||
InjectorProps.parent.name, o.importType(createIdentifier(Identifiers.Injector)))],
|
||||
parent: o.importExpr(
|
||||
resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
||||
createIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
||||
parentArgs: parentArgs,
|
||||
builders: [{methods}, this]
|
||||
});
|
||||
@ -158,7 +160,7 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
||||
let result: o.Expression;
|
||||
if (isPresent(provider.useExisting)) {
|
||||
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||
result = this._getDependency({token: provider.useExisting});
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
const deps = provider.deps || provider.useFactory.diDeps;
|
||||
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||
@ -215,13 +217,12 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
}
|
||||
if (!dep.isSkipSelf) {
|
||||
if (dep.token &&
|
||||
(dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference ||
|
||||
dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) {
|
||||
(tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver))) {
|
||||
result = o.THIS_EXPR;
|
||||
}
|
||||
if (!result) {
|
||||
result = this._instances.get(dep.token.reference);
|
||||
result = this._instances.get(tokenReference(dep.token));
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {Injectable, NgModule, Type} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {ReflectorReader, reflector} from './private_import_core';
|
||||
|
||||
@ -25,7 +26,8 @@ export class NgModuleResolver {
|
||||
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
|
||||
|
||||
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)) {
|
||||
return ngModuleMeta;
|
||||
|
@ -7,15 +7,16 @@
|
||||
*/
|
||||
|
||||
|
||||
import {identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||
import * as o from './output_ast';
|
||||
import {ImportGenerator} from './path_util';
|
||||
import {ImportResolver} from './path_util';
|
||||
|
||||
export class JavaScriptEmitter implements OutputEmitter {
|
||||
constructor(private _importGenerator: ImportGenerator) {}
|
||||
constructor(private _importGenerator: ImportResolver) {}
|
||||
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||
const converter = new JsEmitterVisitor(moduleUrl);
|
||||
const ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||
@ -25,7 +26,7 @@ export class JavaScriptEmitter implements OutputEmitter {
|
||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||
srcParts.push(
|
||||
`var ${prefix} = req` +
|
||||
`uire('${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}');`);
|
||||
`uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`);
|
||||
});
|
||||
srcParts.push(ctx.toSource());
|
||||
return srcParts.join('\n');
|
||||
@ -38,18 +39,21 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
constructor(private _moduleUrl: string) { super(); }
|
||||
|
||||
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}`);
|
||||
}
|
||||
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
|
||||
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(moduleUrl);
|
||||
if (isBlank(prefix)) {
|
||||
prefix = `import${this.importsWithPrefixes.size}`;
|
||||
this.importsWithPrefixes.set(ast.value.moduleUrl, prefix);
|
||||
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||
}
|
||||
ctx.print(`${prefix}.`);
|
||||
}
|
||||
ctx.print(ast.value.name);
|
||||
ctx.print(name);
|
||||
return null;
|
||||
}
|
||||
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(
|
||||
public value: CompileIdentifierMetadata, public typeParams: Type[] = null,
|
||||
public value: Expression, public typeParams: Type[] = null,
|
||||
modifiers: TypeModifier[] = null) {
|
||||
super(modifiers);
|
||||
}
|
||||
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 {
|
||||
visitBuiltintType(type: BuiltinType, context: any): any;
|
||||
visitExternalType(type: ExternalType, context: any): any;
|
||||
visitExpressionType(type: ExpressionType, context: any): any;
|
||||
visitArrayType(type: ArrayType, context: any): any;
|
||||
visitMapType(type: MapType, context: any): any;
|
||||
}
|
||||
@ -876,8 +876,14 @@ export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = n
|
||||
|
||||
export function importType(
|
||||
id: CompileIdentifierMetadata, typeParams: Type[] = null,
|
||||
typeModifiers: TypeModifier[] = null): ExternalType {
|
||||
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null;
|
||||
typeModifiers: TypeModifier[] = null): ExpressionType {
|
||||
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 {
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {identifierName} from '../compile_metadata';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {sanitizeIdentifier} from '../util';
|
||||
|
||||
import {EmitterVisitorContext} from './abstract_emitter';
|
||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||
@ -52,8 +52,8 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
if (id === -1) {
|
||||
id = this._evalArgValues.length;
|
||||
this._evalArgValues.push(value);
|
||||
const name = isPresent(ast.value.name) ? sanitizeIdentifier(ast.value.name) : 'val';
|
||||
this._evalArgNames.push(sanitizeIdentifier(`jit_${name}${id}`));
|
||||
const name = identifierName(ast.value) || 'val';
|
||||
this._evalArgNames.push(`jit_${name}${id}`);
|
||||
}
|
||||
ctx.print(this._evalArgNames[id]);
|
||||
return null;
|
||||
|
@ -6,30 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// asset:<package-name>/<realm>/<path-to-module>
|
||||
const _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/;
|
||||
|
||||
/**
|
||||
* Interface that defines how import statements should be generated.
|
||||
*/
|
||||
export abstract class ImportGenerator {
|
||||
static parseAssetUrl(url: string): AssetUrl { return AssetUrl.parse(url); }
|
||||
|
||||
abstract getImportPath(moduleUrlStr: string, importedUrlStr: string): string;
|
||||
}
|
||||
|
||||
export class AssetUrl {
|
||||
static parse(url: string, allowNonMatching: boolean = true): AssetUrl {
|
||||
const match = url.match(_ASSET_URL_RE);
|
||||
if (match !== null) {
|
||||
return new AssetUrl(match[1], match[2], match[3]);
|
||||
}
|
||||
if (allowNonMatching) {
|
||||
return null;
|
||||
}
|
||||
throw new Error(`Url ${url} is not a valid asset: url`);
|
||||
}
|
||||
|
||||
constructor(public packageName: string, public firstLevelDir: string, public modulePath: string) {
|
||||
}
|
||||
export abstract class ImportResolver {
|
||||
/**
|
||||
* Converts a file path to a module name that can be used as an `import.
|
||||
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
||||
*/
|
||||
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string;
|
||||
}
|
||||
|
@ -7,14 +7,14 @@
|
||||
*/
|
||||
|
||||
|
||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||
import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||
import * as o from './output_ast';
|
||||
import {ImportGenerator} from './path_util';
|
||||
import {ImportResolver} from './path_util';
|
||||
|
||||
const _debugModuleUrl = 'asset://debug/lib';
|
||||
const _debugModuleUrl = '/debug/lib';
|
||||
|
||||
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
|
||||
string {
|
||||
@ -37,7 +37,7 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
|
||||
}
|
||||
|
||||
export class TypeScriptEmitter implements OutputEmitter {
|
||||
constructor(private _importGenerator: ImportGenerator) {}
|
||||
constructor(private _importGenerator: ImportResolver) {}
|
||||
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||
const converter = new _TsEmitterVisitor(moduleUrl);
|
||||
const ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||
@ -47,7 +47,7 @@ export class TypeScriptEmitter implements OutputEmitter {
|
||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||
srcParts.push(
|
||||
`imp` +
|
||||
`ort * as ${prefix} from '${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}';`);
|
||||
`ort * as ${prefix} from '${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}';`);
|
||||
});
|
||||
srcParts.push(ctx.toSource());
|
||||
return srcParts.join('\n');
|
||||
@ -271,8 +271,13 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
return null;
|
||||
}
|
||||
|
||||
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
|
||||
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||
visitExpressionType(ast: o.ExpressionType, ctx: EmitterVisitorContext): any {
|
||||
ast.value.visitExpression(this, ctx);
|
||||
if (isPresent(ast.typeParams) && ast.typeParams.length > 0) {
|
||||
ctx.print(`<`);
|
||||
this.visitAllObjects(type => type.visitType(this, ctx), ast.typeParams, ctx, ',');
|
||||
ctx.print(`>`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -317,14 +322,16 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
|
||||
private _visitIdentifier(
|
||||
value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void {
|
||||
if (isBlank(value.name)) {
|
||||
const name = identifierName(value);
|
||||
const moduleUrl = identifierModuleUrl(value);
|
||||
if (isBlank(name)) {
|
||||
throw new Error(`Internal error: unknown identifier ${value}`);
|
||||
}
|
||||
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
||||
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(moduleUrl);
|
||||
if (isBlank(prefix)) {
|
||||
prefix = `import${this.importsWithPrefixes.size}`;
|
||||
this.importsWithPrefixes.set(value.moduleUrl, prefix);
|
||||
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||
}
|
||||
ctx.print(`${prefix}.`);
|
||||
}
|
||||
@ -333,7 +340,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
ctx.print('.');
|
||||
ctx.print(value.reference.members.join('.'));
|
||||
} else {
|
||||
ctx.print(value.name);
|
||||
ctx.print(name);
|
||||
}
|
||||
if (isPresent(typeParams) && typeParams.length > 0) {
|
||||
ctx.print(`<`);
|
||||
|
@ -30,12 +30,10 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
||||
visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); }
|
||||
|
||||
visitOther(value: any, type: o.Type): o.Expression {
|
||||
if (value instanceof CompileIdentifierMetadata) {
|
||||
return o.importExpr(value);
|
||||
} else if (value instanceof o.Expression) {
|
||||
if (value instanceof o.Expression) {
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`Illegal state: Don't now how to compile value ${value}`);
|
||||
return o.importExpr({reference: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {Injectable, Pipe, Type, resolveForwardRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {ReflectorReader, reflector} from './private_import_core';
|
||||
|
||||
@ -37,7 +38,7 @@ export class PipeResolver {
|
||||
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
|
||||
const metas = this._reflector.annotations(resolveForwardRef(type));
|
||||
if (isPresent(metas)) {
|
||||
const annotation = metas.find(_isPipeMetadata);
|
||||
const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
|
||||
if (isPresent(annotation)) {
|
||||
return annotation;
|
||||
}
|
||||
|
@ -73,3 +73,6 @@ export type ComponentStillLoadingError = typeof r._ComponentStillLoadingError;
|
||||
export const ComponentStillLoadingError: typeof r.ComponentStillLoadingError =
|
||||
r.ComponentStillLoadingError;
|
||||
export const AnimationTransition: typeof r.AnimationTransition = r.AnimationTransition;
|
||||
export type SetterFn = typeof r._SetterFn;
|
||||
export type GetterFn = typeof r._GetterFn;
|
||||
export type MethodFn = typeof r._MethodFn;
|
||||
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
|
||||
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
|
||||
|
||||
@ -31,9 +31,9 @@ export class ProviderViewContext {
|
||||
constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) {
|
||||
this.viewQueries = _getViewQueries(component);
|
||||
this.viewProviders = new Map<any, boolean>();
|
||||
_normalizeProviders(component.viewProviders, sourceSpan, this.errors).forEach((provider) => {
|
||||
if (isBlank(this.viewProviders.get(provider.token.reference))) {
|
||||
this.viewProviders.set(provider.token.reference, true);
|
||||
component.viewProviders.forEach((provider) => {
|
||||
if (isBlank(this.viewProviders.get(tokenReference(provider.token)))) {
|
||||
this.viewProviders.set(tokenReference(provider.token), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -62,17 +62,14 @@ export class ProviderElementContext {
|
||||
Array.from(this._allProviders.values()).forEach((provider) => {
|
||||
this._addQueryReadsTo(provider.token, queriedTokens);
|
||||
});
|
||||
refs.forEach((refAst) => {
|
||||
this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens);
|
||||
});
|
||||
if (isPresent(
|
||||
queriedTokens.get(resolveIdentifierToken(Identifiers.ViewContainerRef).reference))) {
|
||||
refs.forEach((refAst) => { this._addQueryReadsTo({value: refAst.name}, queriedTokens); });
|
||||
if (isPresent(queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef)))) {
|
||||
this._hasViewContainer = true;
|
||||
}
|
||||
|
||||
// create the providers that we know are eager first
|
||||
Array.from(this._allProviders.values()).forEach((provider) => {
|
||||
const eager = provider.eager || isPresent(queriedTokens.get(provider.token.reference));
|
||||
const eager = provider.eager || isPresent(queriedTokens.get(tokenReference(provider.token)));
|
||||
if (eager) {
|
||||
this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
|
||||
}
|
||||
@ -104,8 +101,8 @@ export class ProviderElementContext {
|
||||
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
|
||||
this._getQueriesFor(token).forEach((query) => {
|
||||
const queryReadToken = query.read || token;
|
||||
if (isBlank(queryReadTokens.get(queryReadToken.reference))) {
|
||||
queryReadTokens.set(queryReadToken.reference, true);
|
||||
if (isBlank(queryReadTokens.get(tokenReference(queryReadToken)))) {
|
||||
queryReadTokens.set(tokenReference(queryReadToken), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -116,7 +113,7 @@ export class ProviderElementContext {
|
||||
let distance = 0;
|
||||
let queries: CompileQueryMetadata[];
|
||||
while (currentEl !== null) {
|
||||
queries = currentEl._contentQueries.get(token.reference);
|
||||
queries = currentEl._contentQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries.filter((query) => query.descendants || distance <= 1));
|
||||
}
|
||||
@ -125,7 +122,7 @@ export class ProviderElementContext {
|
||||
}
|
||||
currentEl = currentEl._parent;
|
||||
}
|
||||
queries = this.viewContext.viewQueries.get(token.reference);
|
||||
queries = this.viewContext.viewQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries);
|
||||
}
|
||||
@ -136,7 +133,7 @@ export class ProviderElementContext {
|
||||
private _getOrCreateLocalProvider(
|
||||
requestingProviderType: ProviderAstType, token: CompileTokenMetadata,
|
||||
eager: boolean): ProviderAst {
|
||||
const resolvedProvider = this._allProviders.get(token.reference);
|
||||
const resolvedProvider = this._allProviders.get(tokenReference(token));
|
||||
if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.PublicService) &&
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
||||
@ -145,24 +142,23 @@ export class ProviderElementContext {
|
||||
resolvedProvider.providerType === ProviderAstType.Builtin)) {
|
||||
return null;
|
||||
}
|
||||
let transformedProviderAst = this._transformedProviders.get(token.reference);
|
||||
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
||||
if (isPresent(transformedProviderAst)) {
|
||||
return transformedProviderAst;
|
||||
}
|
||||
if (isPresent(this._seenProviders.get(token.reference))) {
|
||||
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
||||
this.viewContext.errors.push(new ProviderError(
|
||||
`Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan));
|
||||
`Cannot instantiate cyclic dependency! ${tokenName(token)}`, this._sourceSpan));
|
||||
return null;
|
||||
}
|
||||
this._seenProviders.set(token.reference, true);
|
||||
this._seenProviders.set(tokenReference(token), true);
|
||||
const transformedProviders = resolvedProvider.providers.map((provider) => {
|
||||
let transformedUseValue = provider.useValue;
|
||||
let transformedUseExisting = provider.useExisting;
|
||||
let transformedDeps: CompileDiDependencyMetadata[];
|
||||
if (isPresent(provider.useExisting)) {
|
||||
const existingDiDep = this._getDependency(
|
||||
resolvedProvider.providerType,
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}), eager);
|
||||
resolvedProvider.providerType, {token: provider.useExisting}, eager);
|
||||
if (isPresent(existingDiDep.token)) {
|
||||
transformedUseExisting = existingDiDep.token;
|
||||
} else {
|
||||
@ -186,7 +182,7 @@ export class ProviderElementContext {
|
||||
});
|
||||
transformedProviderAst =
|
||||
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
|
||||
this._transformedProviders.set(token.reference, transformedProviderAst);
|
||||
this._transformedProviders.set(tokenReference(token), transformedProviderAst);
|
||||
return transformedProviderAst;
|
||||
}
|
||||
|
||||
@ -195,28 +191,25 @@ export class ProviderElementContext {
|
||||
eager: boolean = null): CompileDiDependencyMetadata {
|
||||
if (dep.isAttribute) {
|
||||
const attrValue = this._attrs[dep.token.value];
|
||||
return new CompileDiDependencyMetadata(
|
||||
{isValue: true, value: attrValue == null ? null : attrValue});
|
||||
return {isValue: true, value: attrValue == null ? null : attrValue};
|
||||
}
|
||||
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtints
|
||||
if ((requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.Component)) {
|
||||
if (dep.token.reference === resolveIdentifierToken(Identifiers.Renderer).reference ||
|
||||
dep.token.reference === resolveIdentifierToken(Identifiers.ElementRef).reference ||
|
||||
dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference ||
|
||||
dep.token.reference === resolveIdentifierToken(Identifiers.TemplateRef).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Renderer) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ElementRef) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.TemplateRef)) {
|
||||
return dep;
|
||||
}
|
||||
if (dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ViewContainerRef).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.ViewContainerRef)) {
|
||||
this._hasViewContainer = true;
|
||||
}
|
||||
}
|
||||
// access the injector
|
||||
if (dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector)) {
|
||||
return dep;
|
||||
}
|
||||
// access providers
|
||||
@ -238,7 +231,7 @@ export class ProviderElementContext {
|
||||
}
|
||||
if (dep.isSelf) {
|
||||
if (!result && dep.isOptional) {
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null});
|
||||
result = {isValue: true, value: null};
|
||||
}
|
||||
} else {
|
||||
// check parent elements
|
||||
@ -252,20 +245,18 @@ export class ProviderElementContext {
|
||||
}
|
||||
// check @Host restriction
|
||||
if (!result) {
|
||||
if (!dep.isHost || this.viewContext.component.type.isHost ||
|
||||
this.viewContext.component.type.reference === dep.token.reference ||
|
||||
isPresent(this.viewContext.viewProviders.get(dep.token.reference))) {
|
||||
if (!dep.isHost || this.viewContext.component.isHost ||
|
||||
this.viewContext.component.type.reference === tokenReference(dep.token) ||
|
||||
isPresent(this.viewContext.viewProviders.get(tokenReference(dep.token)))) {
|
||||
result = dep;
|
||||
} else {
|
||||
result = dep.isOptional ?
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null}) :
|
||||
null;
|
||||
result = dep.isOptional ? result = {isValue: true, value: null} : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
this.viewContext.errors.push(
|
||||
new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan));
|
||||
new ProviderError(`No provider for ${tokenName(dep.token)}`, this._sourceSpan));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -282,17 +273,14 @@ export class NgModuleProviderAnalyzer {
|
||||
ngModule: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[],
|
||||
sourceSpan: ParseSourceSpan) {
|
||||
this._allProviders = new Map<any, ProviderAst>();
|
||||
const ngModuleTypes = ngModule.transitiveModule.modules.map((moduleMeta) => moduleMeta.type);
|
||||
ngModuleTypes.forEach((ngModuleType: CompileTypeMetadata) => {
|
||||
const ngModuleProvider = new CompileProviderMetadata(
|
||||
{token: new CompileTokenMetadata({identifier: ngModuleType}), useClass: ngModuleType});
|
||||
ngModule.transitiveModule.modules.forEach((ngModuleType: CompileTypeMetadata) => {
|
||||
const ngModuleProvider = {token: {identifier: ngModuleType}, useClass: ngModuleType};
|
||||
_resolveProviders(
|
||||
[ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors,
|
||||
this._allProviders);
|
||||
});
|
||||
_resolveProviders(
|
||||
_normalizeProviders(
|
||||
ngModule.transitiveModule.providers.concat(extraProviders), sourceSpan, this._errors),
|
||||
ngModule.transitiveModule.providers.map(entry => entry.provider).concat(extraProviders),
|
||||
ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders);
|
||||
}
|
||||
|
||||
@ -308,28 +296,28 @@ export class NgModuleProviderAnalyzer {
|
||||
}
|
||||
|
||||
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst {
|
||||
const resolvedProvider = this._allProviders.get(token.reference);
|
||||
const resolvedProvider = this._allProviders.get(tokenReference(token));
|
||||
if (!resolvedProvider) {
|
||||
return null;
|
||||
}
|
||||
let transformedProviderAst = this._transformedProviders.get(token.reference);
|
||||
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
||||
if (isPresent(transformedProviderAst)) {
|
||||
return transformedProviderAst;
|
||||
}
|
||||
if (isPresent(this._seenProviders.get(token.reference))) {
|
||||
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
||||
this._errors.push(new ProviderError(
|
||||
`Cannot instantiate cyclic dependency! ${token.name}`, resolvedProvider.sourceSpan));
|
||||
`Cannot instantiate cyclic dependency! ${tokenName(token)}`,
|
||||
resolvedProvider.sourceSpan));
|
||||
return null;
|
||||
}
|
||||
this._seenProviders.set(token.reference, true);
|
||||
this._seenProviders.set(tokenReference(token), true);
|
||||
const transformedProviders = resolvedProvider.providers.map((provider) => {
|
||||
let transformedUseValue = provider.useValue;
|
||||
let transformedUseExisting = provider.useExisting;
|
||||
let transformedDeps: CompileDiDependencyMetadata[];
|
||||
if (isPresent(provider.useExisting)) {
|
||||
const existingDiDep = this._getDependency(
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}), eager,
|
||||
resolvedProvider.sourceSpan);
|
||||
const existingDiDep =
|
||||
this._getDependency({token: provider.useExisting}, eager, resolvedProvider.sourceSpan);
|
||||
if (isPresent(existingDiDep.token)) {
|
||||
transformedUseExisting = existingDiDep.token;
|
||||
} else {
|
||||
@ -353,7 +341,7 @@ export class NgModuleProviderAnalyzer {
|
||||
});
|
||||
transformedProviderAst =
|
||||
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
|
||||
this._transformedProviders.set(token.reference, transformedProviderAst);
|
||||
this._transformedProviders.set(tokenReference(token), transformedProviderAst);
|
||||
return transformedProviderAst;
|
||||
}
|
||||
|
||||
@ -363,9 +351,8 @@ export class NgModuleProviderAnalyzer {
|
||||
let foundLocal = false;
|
||||
if (!dep.isSkipSelf && isPresent(dep.token)) {
|
||||
// access the injector
|
||||
if (dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference ||
|
||||
dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver)) {
|
||||
foundLocal = true;
|
||||
// access providers
|
||||
} else if (isPresent(this._getOrCreateLocalProvider(dep.token, eager))) {
|
||||
@ -375,10 +362,10 @@ export class NgModuleProviderAnalyzer {
|
||||
let result: CompileDiDependencyMetadata = dep;
|
||||
if (dep.isSelf && !foundLocal) {
|
||||
if (dep.isOptional) {
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null});
|
||||
result = {isValue: true, value: null};
|
||||
} else {
|
||||
this._errors.push(
|
||||
new ProviderError(`No provider for ${dep.token.name}`, requestorSourceSpan));
|
||||
new ProviderError(`No provider for ${tokenName(dep.token)}`, requestorSourceSpan));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -389,7 +376,7 @@ function _transformProvider(
|
||||
provider: CompileProviderMetadata,
|
||||
{useExisting, useValue, deps}:
|
||||
{useExisting: CompileTokenMetadata, useValue: any, deps: CompileDiDependencyMetadata[]}) {
|
||||
return new CompileProviderMetadata({
|
||||
return {
|
||||
token: provider.token,
|
||||
useClass: provider.useClass,
|
||||
useExisting: useExisting,
|
||||
@ -397,7 +384,7 @@ function _transformProvider(
|
||||
useValue: useValue,
|
||||
deps: deps,
|
||||
multi: provider.multi
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function _transformProviderAst(
|
||||
@ -408,44 +395,13 @@ function _transformProviderAst(
|
||||
provider.providerType, provider.lifecycleHooks, provider.sourceSpan);
|
||||
}
|
||||
|
||||
function _normalizeProviders(
|
||||
providers: Array<CompileProviderMetadata|CompileTypeMetadata|any[]>,
|
||||
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
|
||||
targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] {
|
||||
if (!targetProviders) {
|
||||
targetProviders = [];
|
||||
}
|
||||
if (isPresent(providers)) {
|
||||
providers.forEach((provider) => {
|
||||
if (Array.isArray(provider)) {
|
||||
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
|
||||
} else {
|
||||
let normalizeProvider: CompileProviderMetadata;
|
||||
if (provider instanceof CompileProviderMetadata) {
|
||||
normalizeProvider = provider;
|
||||
} else if (provider instanceof CompileTypeMetadata) {
|
||||
normalizeProvider = new CompileProviderMetadata(
|
||||
{token: new CompileTokenMetadata({identifier: provider}), useClass: provider});
|
||||
} else {
|
||||
targetErrors.push(new ProviderError(`Unknown provider type ${provider}`, sourceSpan));
|
||||
}
|
||||
if (isPresent(normalizeProvider)) {
|
||||
targetProviders.push(normalizeProvider);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return targetProviders;
|
||||
}
|
||||
|
||||
|
||||
function _resolveProvidersFromDirectives(
|
||||
directives: CompileDirectiveSummary[], sourceSpan: ParseSourceSpan,
|
||||
targetErrors: ParseError[]): Map<any, ProviderAst> {
|
||||
const providersByToken = new Map<any, ProviderAst>();
|
||||
directives.forEach((directive) => {
|
||||
const dirProvider = new CompileProviderMetadata(
|
||||
{token: new CompileTokenMetadata({identifier: directive.type}), useClass: directive.type});
|
||||
const dirProvider:
|
||||
CompileProviderMetadata = {token: {identifier: directive.type}, useClass: directive.type};
|
||||
_resolveProviders(
|
||||
[dirProvider],
|
||||
directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true,
|
||||
@ -457,11 +413,11 @@ function _resolveProvidersFromDirectives(
|
||||
directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
|
||||
directivesWithComponentFirst.forEach((directive) => {
|
||||
_resolveProviders(
|
||||
_normalizeProviders(directive.providers, sourceSpan, targetErrors),
|
||||
ProviderAstType.PublicService, false, sourceSpan, targetErrors, providersByToken);
|
||||
directive.providers, ProviderAstType.PublicService, false, sourceSpan, targetErrors,
|
||||
providersByToken);
|
||||
_resolveProviders(
|
||||
_normalizeProviders(directive.viewProviders, sourceSpan, targetErrors),
|
||||
ProviderAstType.PrivateService, false, sourceSpan, targetErrors, providersByToken);
|
||||
directive.viewProviders, ProviderAstType.PrivateService, false, sourceSpan, targetErrors,
|
||||
providersByToken);
|
||||
});
|
||||
return providersByToken;
|
||||
}
|
||||
@ -471,21 +427,21 @@ function _resolveProviders(
|
||||
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
|
||||
targetProvidersByToken: Map<any, ProviderAst>) {
|
||||
providers.forEach((provider) => {
|
||||
let resolvedProvider = targetProvidersByToken.get(provider.token.reference);
|
||||
if (isPresent(resolvedProvider) && resolvedProvider.multiProvider !== provider.multi) {
|
||||
let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token));
|
||||
if (isPresent(resolvedProvider) && !!resolvedProvider.multiProvider !== !!provider.multi) {
|
||||
targetErrors.push(new ProviderError(
|
||||
`Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`,
|
||||
`Mixing multi and non multi provider is not possible for token ${tokenName(resolvedProvider.token)}`,
|
||||
sourceSpan));
|
||||
}
|
||||
if (!resolvedProvider) {
|
||||
const lifecycleHooks =
|
||||
provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ?
|
||||
provider.token.identifier.lifecycleHooks :
|
||||
const lifecycleHooks = provider.token.identifier &&
|
||||
(<CompileTypeMetadata>provider.token.identifier).lifecycleHooks ?
|
||||
(<CompileTypeMetadata>provider.token.identifier).lifecycleHooks :
|
||||
[];
|
||||
resolvedProvider = new ProviderAst(
|
||||
provider.token, provider.multi, eager || lifecycleHooks.length > 0, [provider],
|
||||
providerType, lifecycleHooks, sourceSpan);
|
||||
targetProvidersByToken.set(provider.token.reference, resolvedProvider);
|
||||
targetProvidersByToken.set(tokenReference(provider.token), resolvedProvider);
|
||||
} else {
|
||||
if (!provider.multi) {
|
||||
resolvedProvider.providers.length = 0;
|
||||
@ -517,10 +473,10 @@ function _getContentQueries(directives: CompileDirectiveSummary[]):
|
||||
|
||||
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
|
||||
query.selectors.forEach((token: CompileTokenMetadata) => {
|
||||
let entry = map.get(token.reference);
|
||||
let entry = map.get(tokenReference(token));
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(token.reference, entry);
|
||||
map.set(tokenReference(token), entry);
|
||||
}
|
||||
entry.push(query);
|
||||
});
|
||||
|
@ -7,7 +7,8 @@
|
||||
*/
|
||||
|
||||
import {Injectable, ViewEncapsulation} from '@angular/core';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata} from './compile_metadata';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||
import * as o from './output/output_ast';
|
||||
import {ShadowCss} from './shadow_css';
|
||||
import {UrlResolver} from './url_resolver';
|
||||
@ -18,7 +19,7 @@ const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
export class StylesCompileDependency {
|
||||
constructor(
|
||||
public moduleUrl: string, public isShimmed: boolean,
|
||||
public name: string, public moduleUrl: string, public isShimmed: boolean,
|
||||
public valuePlaceholder: CompileIdentifierMetadata) {}
|
||||
}
|
||||
|
||||
@ -47,7 +48,7 @@ export class StyleCompiler {
|
||||
comp, new CompileStylesheetMetadata({
|
||||
styles: comp.template.styles,
|
||||
styleUrls: comp.template.styleUrls,
|
||||
moduleUrl: comp.type.moduleUrl
|
||||
moduleUrl: identifierModuleUrl(comp.type)
|
||||
}),
|
||||
true);
|
||||
comp.template.externalStylesheets.forEach((stylesheetMeta) => {
|
||||
@ -65,8 +66,9 @@ export class StyleCompiler {
|
||||
stylesheet.styles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
|
||||
const dependencies: StylesCompileDependency[] = [];
|
||||
for (let i = 0; i < stylesheet.styleUrls.length; i++) {
|
||||
const identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)});
|
||||
dependencies.push(new StylesCompileDependency(stylesheet.styleUrls[i], shim, identifier));
|
||||
const identifier: CompileIdentifierMetadata = {reference: null};
|
||||
dependencies.push(new StylesCompileDependency(
|
||||
getStylesVarName(null), stylesheet.styleUrls[i], shim, identifier));
|
||||
styleExpressions.push(new o.ExternalExpr(identifier));
|
||||
}
|
||||
// styles variable contains plain strings and arrays of other styles arrays (recursive),
|
||||
@ -87,7 +89,7 @@ export class StyleCompiler {
|
||||
function getStylesVarName(component: CompileDirectiveMetadata): string {
|
||||
let result = `styles`;
|
||||
if (component) {
|
||||
result += `_${component.type.name}`;
|
||||
result += `_${identifierName(component.type)}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
14
modules/@angular/compiler/src/summary_resolver.ts
Normal file
14
modules/@angular/compiler/src/summary_resolver.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @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 {Injectable} from '@angular/core';
|
||||
import {CompileTypeSummary} from './compile_metadata';
|
||||
|
||||
@Injectable()
|
||||
export class SummaryResolver {
|
||||
resolveSummary(reference: any): CompileTypeSummary { return null; }
|
||||
}
|
@ -8,12 +8,12 @@
|
||||
|
||||
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateMetadata, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, removeIdentifierDuplicates} from '../compile_metadata';
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateMetadata, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
||||
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
||||
import {Parser} from '../expression_parser/parser';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
|
||||
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
|
||||
import {Identifiers, createIdentifierToken, identifierToken} from '../identifiers';
|
||||
import * as html from '../ml_parser/ast';
|
||||
import {ParseTreeResult} from '../ml_parser/html_parser';
|
||||
import {expandNodes} from '../ml_parser/icu_ast_expander';
|
||||
@ -546,7 +546,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
let component: CompileDirectiveSummary = null;
|
||||
const directiveAsts = directives.map((directive) => {
|
||||
const sourceSpan = new ParseSourceSpan(
|
||||
elementSourceSpan.start, elementSourceSpan.end, `Directive ${directive.type.name}`);
|
||||
elementSourceSpan.start, elementSourceSpan.end,
|
||||
`Directive ${identifierName(directive.type)}`);
|
||||
if (directive.isComponent) {
|
||||
component = directive;
|
||||
}
|
||||
@ -579,7 +580,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
} else if (!component) {
|
||||
let refToken: CompileTokenMetadata = null;
|
||||
if (isTemplateElement) {
|
||||
refToken = resolveIdentifierToken(Identifiers.TemplateRef);
|
||||
refToken = createIdentifierToken(Identifiers.TemplateRef);
|
||||
}
|
||||
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
|
||||
}
|
||||
@ -640,7 +641,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
|
||||
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
||||
return this._findComponentDirectives(directives)
|
||||
.map(directive => directive.directive.type.name);
|
||||
.map(directive => identifierName(directive.directive.type));
|
||||
}
|
||||
|
||||
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
||||
|
@ -10,9 +10,6 @@ import {Inject, Injectable, PACKAGE_ROOT_URL} from '@angular/core';
|
||||
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
|
||||
|
||||
const _ASSET_SCHEME = 'asset:';
|
||||
|
||||
/**
|
||||
* Create a {@link UrlResolver} with no package prefix.
|
||||
*/
|
||||
@ -21,7 +18,7 @@ export function createUrlResolverWithoutPackagePrefix(): UrlResolver {
|
||||
}
|
||||
|
||||
export function createOfflineCompileUrlResolver(): UrlResolver {
|
||||
return new UrlResolver(_ASSET_SCHEME);
|
||||
return new UrlResolver('.');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,14 +67,9 @@ export class UrlResolver {
|
||||
if (isPresent(prefix) && isPresent(resolvedParts) &&
|
||||
resolvedParts[_ComponentIndex.Scheme] == 'package') {
|
||||
let path = resolvedParts[_ComponentIndex.Path];
|
||||
if (this._packagePrefix === _ASSET_SCHEME) {
|
||||
const pathSegements = path.split(/\//);
|
||||
resolvedUrl = `asset:${pathSegements[0]}/lib/${pathSegements.slice(1).join('/')}`;
|
||||
} else {
|
||||
prefix = prefix.replace(/\/+$/, '');
|
||||
path = path.replace(/^\/+/, '');
|
||||
return `${prefix}/${path}`;
|
||||
}
|
||||
prefix = prefix.replace(/\/+$/, '');
|
||||
path = path.replace(/^\/+/, '');
|
||||
return `${prefix}/${path}`;
|
||||
}
|
||||
return resolvedUrl;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {isBlank, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||
import {isPrimitive, isStrictStringMap} from './facade/lang';
|
||||
|
||||
export const MODULE_SUFFIX = '';
|
||||
|
||||
@ -35,10 +35,6 @@ function _splitAt(input: string, character: string, defaultValues: string[]): st
|
||||
return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
|
||||
}
|
||||
|
||||
export function sanitizeIdentifier(name: string): string {
|
||||
return name.replace(/\W/g, '_');
|
||||
}
|
||||
|
||||
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
|
||||
if (Array.isArray(value)) {
|
||||
return visitor.visitArray(<any[]>value, context);
|
||||
@ -48,7 +44,7 @@ export function visitValue(value: any, visitor: ValueVisitor, context: any): any
|
||||
return visitor.visitStringMap(<{[key: string]: any}>value, context);
|
||||
}
|
||||
|
||||
if (isBlank(value) || isPrimitive(value)) {
|
||||
if (value == null || isPrimitive(value)) {
|
||||
return visitor.visitPrimitive(value, context);
|
||||
}
|
||||
|
||||
|
19
modules/@angular/compiler/src/version.ts
Normal file
19
modules/@angular/compiler/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,11 +7,11 @@
|
||||
*/
|
||||
|
||||
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, identifierName, tokenName, tokenReference} from '../compile_metadata';
|
||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||
import {DirectiveWrapperCompiler, DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import {Identifiers, createIdentifier, createIdentifierToken, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {convertValueToOutputAst} from '../output/value_util';
|
||||
import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast';
|
||||
@ -20,7 +20,7 @@ import {CompileMethod} from './compile_method';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
import {CompileView, CompileViewRootNode} from './compile_view';
|
||||
import {InjectMethodVars, ViewProperties} from './constants';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
|
||||
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||
|
||||
export class CompileNode {
|
||||
@ -35,7 +35,7 @@ export class CompileNode {
|
||||
|
||||
export class CompileElement extends CompileNode {
|
||||
static createNull(): CompileElement {
|
||||
return new CompileElement(null, null, null, null, null, null, [], [], false, false, [], []);
|
||||
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
|
||||
}
|
||||
|
||||
public compViewExpr: o.Expression = null;
|
||||
@ -57,21 +57,18 @@ export class CompileElement extends CompileNode {
|
||||
sourceAst: TemplateAst, public component: CompileDirectiveSummary,
|
||||
private _directives: CompileDirectiveSummary[],
|
||||
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
|
||||
public hasEmbeddedView: boolean, references: ReferenceAst[],
|
||||
private _targetDependencies:
|
||||
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
||||
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
|
||||
super(parent, view, nodeIndex, renderNode, sourceAst);
|
||||
this.referenceTokens = {};
|
||||
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
|
||||
|
||||
this.elementRef =
|
||||
o.importExpr(resolveIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]);
|
||||
this.instances.set(resolveIdentifierToken(Identifiers.ElementRef).reference, this.elementRef);
|
||||
o.importExpr(createIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]);
|
||||
this.instances.set(resolveIdentifier(Identifiers.ElementRef), this.elementRef);
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.Injector).reference,
|
||||
resolveIdentifier(Identifiers.Injector),
|
||||
o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]));
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.Renderer).reference, o.THIS_EXPR.prop('renderer'));
|
||||
this.instances.set(resolveIdentifier(Identifiers.Renderer), o.THIS_EXPR.prop('renderer'));
|
||||
if (this.hasViewContainer || this.hasEmbeddedView) {
|
||||
this._createViewContainer();
|
||||
}
|
||||
@ -85,41 +82,40 @@ export class CompileElement extends CompileNode {
|
||||
const parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
||||
// private is fine here as no child view will reference a ViewContainer
|
||||
this.view.fields.push(new o.ClassField(
|
||||
fieldName, o.importType(resolveIdentifier(Identifiers.ViewContainer)),
|
||||
fieldName, o.importType(createIdentifier(Identifiers.ViewContainer)),
|
||||
[o.StmtModifier.Private]));
|
||||
const statement =
|
||||
o.THIS_EXPR.prop(fieldName)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ViewContainer)).instantiate([
|
||||
.set(o.importExpr(createIdentifier(Identifiers.ViewContainer)).instantiate([
|
||||
o.literal(this.nodeIndex), o.literal(parentNodeIndex), o.THIS_EXPR, this.renderNode
|
||||
]))
|
||||
.toStmt();
|
||||
this.view.createMethod.addStmt(statement);
|
||||
this.viewContainer = o.THIS_EXPR.prop(fieldName);
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.ViewContainer).reference, this.viewContainer);
|
||||
this.instances.set(resolveIdentifier(Identifiers.ViewContainer), this.viewContainer);
|
||||
this.view.viewContainers.push(this.viewContainer);
|
||||
}
|
||||
|
||||
private _createComponentFactoryResolver() {
|
||||
const entryComponents =
|
||||
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
|
||||
const id = new CompileIdentifierMetadata({name: entryComponent.name});
|
||||
this._targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
||||
const id: CompileIdentifierMetadata = {reference: null};
|
||||
this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
||||
return id;
|
||||
});
|
||||
if (!entryComponents || entryComponents.length === 0) {
|
||||
return;
|
||||
}
|
||||
const createComponentFactoryResolverExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([
|
||||
o.literalArr(entryComponents.map((entryComponent) => o.importExpr(entryComponent))),
|
||||
injectFromViewParentInjector(
|
||||
this.view, resolveIdentifierToken(Identifiers.ComponentFactoryResolver), false)
|
||||
this.view, createIdentifierToken(Identifiers.ComponentFactoryResolver), false)
|
||||
]);
|
||||
const provider = new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.ComponentFactoryResolver),
|
||||
const provider: CompileProviderMetadata = {
|
||||
token: createIdentifierToken(Identifiers.ComponentFactoryResolver),
|
||||
useValue: createComponentFactoryResolverExpr
|
||||
});
|
||||
};
|
||||
// Add ComponentFactoryResolver as first provider as it does not have deps on other providers
|
||||
// ProviderAstType.PrivateService as only the component and its view can see it,
|
||||
// but nobody else
|
||||
@ -141,13 +137,13 @@ export class CompileElement extends CompileNode {
|
||||
this.embeddedView = embeddedView;
|
||||
if (isPresent(embeddedView)) {
|
||||
const createTemplateRefExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.TemplateRef_)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.TemplateRef_)).instantiate([
|
||||
o.THIS_EXPR, o.literal(this.nodeIndex), this.renderNode
|
||||
]);
|
||||
const provider = new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.TemplateRef),
|
||||
const provider: CompileProviderMetadata = {
|
||||
token: createIdentifierToken(Identifiers.TemplateRef),
|
||||
useValue: createTemplateRefExpr
|
||||
});
|
||||
};
|
||||
// Add TemplateRef as first provider as it does not have deps on other providers
|
||||
this._resolvedProvidersArray.unshift(new ProviderAst(
|
||||
provider.token, false, true, [provider], ProviderAstType.Builtin, [],
|
||||
@ -158,13 +154,12 @@ export class CompileElement extends CompileNode {
|
||||
beforeChildren(): void {
|
||||
if (this.hasViewContainer) {
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.ViewContainerRef).reference,
|
||||
this.viewContainer.prop('vcRef'));
|
||||
resolveIdentifier(Identifiers.ViewContainerRef), this.viewContainer.prop('vcRef'));
|
||||
}
|
||||
|
||||
this._resolvedProviders = new Map<any, ProviderAst>();
|
||||
this._resolvedProvidersArray.forEach(
|
||||
provider => this._resolvedProviders.set(provider.token.reference, provider));
|
||||
provider => this._resolvedProviders.set(tokenReference(provider.token), provider));
|
||||
|
||||
// create all the provider instances, some in the view constructor,
|
||||
// some as getters. We rely on the fact that they are already sorted topologically.
|
||||
@ -173,9 +168,7 @@ export class CompileElement extends CompileNode {
|
||||
resolvedProvider.providerType === ProviderAstType.Directive;
|
||||
const providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
||||
if (provider.useExisting) {
|
||||
return this._getDependency(
|
||||
resolvedProvider.providerType,
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||
return this._getDependency(resolvedProvider.providerType, {token: provider.useExisting});
|
||||
} else if (provider.useFactory) {
|
||||
const deps = provider.deps || provider.useFactory.diDeps;
|
||||
const depsExpr =
|
||||
@ -186,10 +179,10 @@ export class CompileElement extends CompileNode {
|
||||
const depsExpr =
|
||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||
if (isDirectiveWrapper) {
|
||||
const directiveWrapperIdentifier = new CompileIdentifierMetadata(
|
||||
{name: DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass)});
|
||||
this._targetDependencies.push(
|
||||
new DirectiveWrapperDependency(provider.useClass, directiveWrapperIdentifier));
|
||||
const directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null};
|
||||
this.view.targetDependencies.push(new DirectiveWrapperDependency(
|
||||
provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass),
|
||||
directiveWrapperIdentifier));
|
||||
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr);
|
||||
} else {
|
||||
return o.importExpr(provider.useClass)
|
||||
@ -199,22 +192,23 @@ export class CompileElement extends CompileNode {
|
||||
return convertValueToOutputAst(provider.useValue);
|
||||
}
|
||||
});
|
||||
const propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this.instances.size}`;
|
||||
const propName =
|
||||
`_${tokenName(resolvedProvider.token)}_${this.nodeIndex}_${this.instances.size}`;
|
||||
const instance = createProviderProperty(
|
||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||
resolvedProvider.eager, this);
|
||||
if (isDirectiveWrapper) {
|
||||
this.directiveWrapperInstance.set(resolvedProvider.token.reference, instance);
|
||||
this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
|
||||
this.instances.set(
|
||||
resolvedProvider.token.reference, DirectiveWrapperExpressions.context(instance));
|
||||
tokenReference(resolvedProvider.token), DirectiveWrapperExpressions.context(instance));
|
||||
} else {
|
||||
this.instances.set(resolvedProvider.token.reference, instance);
|
||||
this.instances.set(tokenReference(resolvedProvider.token), instance);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < this._directives.length; i++) {
|
||||
const directive = this._directives[i];
|
||||
const directiveInstance = this.instances.get(identifierToken(directive.type).reference);
|
||||
const directiveInstance = this.instances.get(tokenReference(identifierToken(directive.type)));
|
||||
directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); });
|
||||
}
|
||||
const queriesWithReads: _QueryWithRead[] = [];
|
||||
@ -227,12 +221,12 @@ export class CompileElement extends CompileNode {
|
||||
const token = this.referenceTokens[varName];
|
||||
let varValue: o.Expression;
|
||||
if (token) {
|
||||
varValue = this.instances.get(token.reference);
|
||||
varValue = this.instances.get(tokenReference(token));
|
||||
} else {
|
||||
varValue = this.renderNode;
|
||||
}
|
||||
this.view.locals.set(varName, varValue);
|
||||
const varToken = new CompileTokenMetadata({value: varName});
|
||||
const varToken = {value: varName};
|
||||
queriesWithReads.push(
|
||||
...this._getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
||||
});
|
||||
@ -240,12 +234,12 @@ export class CompileElement extends CompileNode {
|
||||
let value: o.Expression;
|
||||
if (isPresent(queryWithRead.read.identifier)) {
|
||||
// query for an identifier
|
||||
value = this.instances.get(queryWithRead.read.reference);
|
||||
value = this.instances.get(tokenReference(queryWithRead.read));
|
||||
} else {
|
||||
// query for a reference
|
||||
const token = this.referenceTokens[queryWithRead.read.value];
|
||||
if (isPresent(token)) {
|
||||
value = this.instances.get(token.reference);
|
||||
value = this.instances.get(tokenReference(token));
|
||||
} else {
|
||||
value = this.elementRef;
|
||||
}
|
||||
@ -261,7 +255,7 @@ export class CompileElement extends CompileNode {
|
||||
// Note: afterChildren is called after recursing into children.
|
||||
// This is good so that an injector match in an element that is closer to a requesting element
|
||||
// matches first.
|
||||
const providerExpr = this.instances.get(resolvedProvider.token.reference);
|
||||
const providerExpr = this.instances.get(tokenReference(resolvedProvider.token));
|
||||
// Note: view providers are only visible on the injector of that element.
|
||||
// This is not fully correct as the rules during codegen don't allow a directive
|
||||
// to get hold of a view provdier on the same element. We still do this semantic
|
||||
@ -285,7 +279,7 @@ export class CompileElement extends CompileNode {
|
||||
|
||||
getComponent(): o.Expression {
|
||||
return isPresent(this.component) ?
|
||||
this.instances.get(identifierToken(this.component.type).reference) :
|
||||
this.instances.get(tokenReference(identifierToken(this.component.type))) :
|
||||
null;
|
||||
}
|
||||
|
||||
@ -300,7 +294,7 @@ export class CompileElement extends CompileNode {
|
||||
let distance = 0;
|
||||
let queries: CompileQuery[];
|
||||
while (!currentEl.isNull()) {
|
||||
queries = currentEl._queries.get(token.reference);
|
||||
queries = currentEl._queries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
|
||||
}
|
||||
@ -309,7 +303,7 @@ export class CompileElement extends CompileNode {
|
||||
}
|
||||
currentEl = currentEl.parent;
|
||||
}
|
||||
queries = this.view.componentView.viewQueries.get(token.reference);
|
||||
queries = this.view.componentView.viewQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries);
|
||||
}
|
||||
@ -319,7 +313,7 @@ export class CompileElement extends CompileNode {
|
||||
private _addQuery(queryMeta: CompileQueryMetadata, directiveInstance: o.Expression):
|
||||
CompileQuery {
|
||||
const propName =
|
||||
`_query_${queryMeta.selectors[0].name}_${this.nodeIndex}_${this._queryCount++}`;
|
||||
`_query_${tokenName(queryMeta.selectors[0])}_${this.nodeIndex}_${this._queryCount++}`;
|
||||
const queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
|
||||
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
||||
addQueryToTokenMap(this._queries, query);
|
||||
@ -332,8 +326,7 @@ export class CompileElement extends CompileNode {
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtins with special visibility
|
||||
if (!result) {
|
||||
if (dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
|
||||
if (requestingProviderType === ProviderAstType.Component) {
|
||||
return this.compViewExpr.prop('ref');
|
||||
} else {
|
||||
@ -343,7 +336,7 @@ export class CompileElement extends CompileNode {
|
||||
}
|
||||
// access regular providers on the element
|
||||
if (!result) {
|
||||
const resolvedProvider = this._resolvedProviders.get(dep.token.reference);
|
||||
const resolvedProvider = this._resolvedProviders.get(tokenReference(dep.token));
|
||||
// don't allow directives / public services to access private services.
|
||||
// only components and private services can access private services.
|
||||
if (resolvedProvider && (requestingProviderType === ProviderAstType.Directive ||
|
||||
@ -351,7 +344,7 @@ export class CompileElement extends CompileNode {
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) {
|
||||
return null;
|
||||
}
|
||||
result = this.instances.get(dep.token.reference);
|
||||
result = this.instances.get(tokenReference(dep.token));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -370,8 +363,7 @@ export class CompileElement extends CompileNode {
|
||||
// check parent elements
|
||||
while (!result && !currElement.parent.isNull()) {
|
||||
currElement = currElement.parent;
|
||||
result = currElement._getLocalDependency(
|
||||
ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token}));
|
||||
result = currElement._getLocalDependency(ProviderAstType.PublicService, {token: dep.token});
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
|
||||
|
||||
import {CompilePipeSummary} from '../compile_metadata';
|
||||
import {CompilePipeSummary, tokenReference} from '../compile_metadata';
|
||||
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
@ -42,8 +42,7 @@ export class CompilePipe {
|
||||
constructor(public view: CompileView, public meta: CompilePipeSummary) {
|
||||
this.instance = o.THIS_EXPR.prop(`_pipe_${meta.name}_${view.pipeCount++}`);
|
||||
const deps = this.meta.type.diDeps.map((diDep) => {
|
||||
if (diDep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
|
||||
if (tokenReference(diDep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
|
||||
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
|
||||
}
|
||||
return injectFromViewParentInjector(view, diDep.token, false);
|
||||
@ -69,7 +68,7 @@ export class CompilePipe {
|
||||
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
|
||||
args.length, purePipeProxyInstance,
|
||||
{fields: callingView.fields, ctorStmts: callingView.createMethod});
|
||||
return o.importExpr(resolveIdentifier(Identifiers.castByValue))
|
||||
return o.importExpr(createIdentifier(Identifiers.castByValue))
|
||||
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
|
||||
.callFn(args);
|
||||
} else {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user