Compare commits
187 Commits
2.2.0-beta
...
2.2.2
Author | SHA1 | Date | |
---|---|---|---|
11ed8f56ab | |||
a49acbf027 | |||
8e41910429 | |||
a4ab14bf74 | |||
ea4fc9b421 | |||
0956acee58 | |||
2ca67e1674 | |||
472666fc2b | |||
462316b0f1 | |||
96c2b2cc25 | |||
3d407fc010 | |||
64bd672e3a | |||
ef38676091 | |||
38be2b81c6 | |||
39a71eb0ec | |||
2fe6fb1163 | |||
b5afe51b26 | |||
170525a225 | |||
0c98f45105 | |||
e7025c9423 | |||
8f295287a2 | |||
030facc66a | |||
45af8f6752 | |||
33a79028be | |||
09226d96f8 | |||
6c3166e6e4 | |||
8df328b15a | |||
115f18fa06 | |||
511cd4d182 | |||
87d5d49530 | |||
933caacad3 | |||
efe9c4f35c | |||
5b0f9e2f51 | |||
462879887a | |||
dae0d0fd66 | |||
c7f750dd5a | |||
73de925551 | |||
547c22029a | |||
364642d58c | |||
7b67badc43 | |||
dc1662a447 | |||
b5f433626b | |||
dabaf858d9 | |||
bbc3c9ce0e | |||
1dcf1f484e | |||
583d2833db | |||
f502a768d3 | |||
16303ac487 | |||
6cdc3b5c12 | |||
5c46c493f2 | |||
e02c18049d | |||
e0ce5458a2 | |||
6a5ba0ec81 | |||
828c0d24eb | |||
22536442d6 | |||
845ea235ee | |||
21a4de999b | |||
82b34838bf | |||
2524d510bc | |||
8f5dd1f11e | |||
77ee27c59e | |||
73593d4bf3 | |||
a965d11cce | |||
52be848f94 | |||
69dfcf7385 | |||
785b7b640e | |||
e5a753e111 | |||
768cddbe62 | |||
92f244aa26 | |||
2a4bf9a0df | |||
45ddd6ba78 | |||
7886561997 | |||
752edca81b | |||
1bd858fb43 | |||
fcb4e66493 | |||
ef881475e9 | |||
458ca7112a | |||
2aba8b0ff2 | |||
77dc1ab675 | |||
3052fb234f | |||
79383ce150 | |||
c3c0e2e2a2 | |||
44a142fc02 | |||
3d9d839c6c | |||
69f87ca075 | |||
f224ca1461 | |||
19e869e7c9 | |||
7cab30f85d | |||
73407351e7 | |||
2c110931f8 | |||
2ced2a8a5a | |||
634b3bb88b | |||
4595a61aeb | |||
f80a157b65 | |||
6e35d13fbc | |||
fe35bc34f6 | |||
ad3bf6c54f | |||
a0e9fde653 | |||
3dc61779f0 | |||
09092ac3c2 | |||
778e6ad3b4 | |||
55dc0e4a5f | |||
4708b248d5 | |||
7694f974af | |||
acbf1d859c | |||
f3793b5953 | |||
22c021c57f | |||
d8f23f4b7f | |||
32fcec9fcb | |||
78039b41d6 | |||
89fd54e8e3 | |||
77cbf7f2bb | |||
383f23b578 | |||
2a3f4d7b17 | |||
ec92f4b198 | |||
121e5080aa | |||
fe1d0e29c5 | |||
469010ea8e | |||
f0cdb428f5 | |||
051d74802a | |||
f2bbef3e33 | |||
80d36b8db4 | |||
e3687706c7 | |||
648ce5981b | |||
9c23884da4 | |||
d708a8859c | |||
9ddf9b3d3d | |||
69f006cd89 | |||
4aaae3eada | |||
2e78b76fcf | |||
b2cf379d1c | |||
e25baa08b3 | |||
7103754178 | |||
1a069e8372 | |||
0fc11a43f1 | |||
0e3d655220 | |||
7c5cc9bc41 | |||
5f1dddc5d0 | |||
20a4f9923f | |||
e7c00be19d | |||
74ede9aa9b | |||
d1035da85c | |||
13533d2a30 | |||
953cb50fa5 | |||
3fffcf6645 | |||
d509ee078b | |||
8e221b826f | |||
830a780cb3 | |||
6fda97287e | |||
234c5599f1 | |||
f6710fefeb | |||
bda1909ede | |||
b3e3cd3add | |||
e5fdf4c70a | |||
97471d74b6 | |||
1de04b23b1 | |||
a178bc6c83 | |||
642c1db9ef | |||
579deeb9c5 | |||
bad58824a0 | |||
5494169fb4 | |||
5a3d7a62a2 | |||
a382d6dd20 | |||
52bf188b8f | |||
6f412bb449 | |||
e9fd8645ed | |||
a0aecac0e5 | |||
938ed1c76d | |||
eb8288f76c | |||
0936ceeab4 | |||
e0ad413a8e | |||
3045d02b9a | |||
e86573bac8 | |||
0a94845435 | |||
262bd23b84 | |||
7b8dae19af | |||
7c16ef942e | |||
a318b57257 | |||
fe47e6b783 | |||
091c390032 | |||
e391cacdf9 | |||
32feb8a532 | |||
a664aba2c9 | |||
d520fae70e | |||
fa93fd672e | |||
f4be2f907d | |||
2ea27a76d3 |
176
CHANGELOG.md
176
CHANGELOG.md
@ -1,7 +1,170 @@
|
||||
<a name="2.2.0-beta.1"></a>
|
||||
# [2.2.0-beta.1](https://github.com/angular/angular/compare/2.2.0-beta.0...2.2.0-beta.1) (2016-10-27)
|
||||
<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)
|
||||
|
||||
### Features (summary of all features from 2.2.0-beta.0 - 2.2.0-rc.0 releases)
|
||||
|
||||
* **common:** support narrow forms for month and weekdays in DatePipe ([#12297](https://github.com/angular/angular/issues/12297)) ([f77ab6a](https://github.com/angular/angular/commit/f77ab6a)), closes [#12294](https://github.com/angular/angular/issues/12294)
|
||||
* **core:** map 'for' attribute to 'htmlFor' property ([#10546](https://github.com/angular/angular/issues/10546)) ([634b3bb](https://github.com/angular/angular/commit/634b3bb)), closes [#7516](https://github.com/angular/angular/issues/7516)
|
||||
* **core:** add the find method to QueryList ([7c16ef9](https://github.com/angular/angular/commit/7c16ef9))
|
||||
* **forms:** add hasError and getError to AbstractControlDirective ([#11985](https://github.com/angular/angular/issues/11985)) ([592f40a](https://github.com/angular/angular/commit/592f40a)), closes [#7255](https://github.com/angular/angular/issues/7255)
|
||||
* **forms:** add ng-pending CSS class during async validation ([#11243](https://github.com/angular/angular/issues/11243)) ([97bc971](https://github.com/angular/angular/commit/97bc971)), closes [#10336](https://github.com/angular/angular/issues/10336)
|
||||
* **forms:** add emitEvent to AbstractControl methods ([#11949](https://github.com/angular/angular/issues/11949)) ([b9fc090](https://github.com/angular/angular/commit/b9fc090))
|
||||
* **forms:** make 'parent' a public property of 'AbstractControl' ([#11855](https://github.com/angular/angular/issues/11855)) ([445e592](https://github.com/angular/angular/commit/445e592))
|
||||
* **forms:** Validator.pattern accepts a RegExp ([#12323](https://github.com/angular/angular/issues/12323)) ([bf60418](https://github.com/angular/angular/commit/bf60418))
|
||||
* **router:** add a provider making angular1/angular2 integration easier ([#12769](https://github.com/angular/angular/issues/12769)) ([6e35d13](https://github.com/angular/angular/commit/6e35d13))
|
||||
* **router:** add support for custom url matchers ([7340735](https://github.com/angular/angular/commit/7340735)), closes [#12442](https://github.com/angular/angular/issues/12442) [#12772](https://github.com/angular/angular/issues/12772)
|
||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
||||
* **router:** add support for ng1/ng2 migration ([#12160](https://github.com/angular/angular/issues/12160)) ([8b9ab44](https://github.com/angular/angular/commit/8b9ab44))
|
||||
* **upgrade:** add support for AoT compiled upgrade applications ([d6791ff](https://github.com/angular/angular/commit/d6791ff)), closes [#12239](https://github.com/angular/angular/issues/12239)
|
||||
* **upgrade:** add support for `require` in UpgradeComponent ([fe1d0e2](https://github.com/angular/angular/commit/fe1d0e2))
|
||||
* **upgrade:** add/improve support for lifecycle hooks in UpgradeComponent ([469010e](https://github.com/angular/angular/commit/469010e))
|
||||
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** introduce direct rendering ([9c23884](https://github.com/angular/angular/commit/9c23884))
|
||||
* **core:** don’t use `DomAdapter` nor zone for regular events ([648ce59](https://github.com/angular/angular/commit/648ce59))
|
||||
* **core:** use `array.push` / `array.pop` instead of `splice` if possible ([0fc11a4](https://github.com/angular/angular/commit/0fc11a4))
|
||||
* **platform-browser:** cache plugin resolution in the EventManager ([73593d4](https://github.com/angular/angular/commit/73593d4)), closes [#12824](https://github.com/angular/angular/issues/12824)
|
||||
* **platform-browser:** don’t use `DomAdapter` any more ([d708a88](https://github.com/angular/angular/commit/d708a88))
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** allow animations to be destroyed manually ([#12719](https://github.com/angular/angular/issues/12719)) ([fe35bc3](https://github.com/angular/angular/commit/fe35bc3)), closes [#12456](https://github.com/angular/angular/issues/12456)
|
||||
* **animations:** always normalize style properties and values during compilation ([#12755](https://github.com/angular/angular/issues/12755)) ([a0e9fde](https://github.com/angular/angular/commit/a0e9fde)), closes [#11582](https://github.com/angular/angular/issues/11582) [#12481](https://github.com/angular/angular/issues/12481)
|
||||
* **animations:** always trigger animations after the change detection check ([#12713](https://github.com/angular/angular/issues/12713)) ([383f23b](https://github.com/angular/angular/commit/383f23b))
|
||||
* **animations:** ensure animations work with web-workers ([#12656](https://github.com/angular/angular/issues/12656)) ([19e869e](https://github.com/angular/angular/commit/19e869e))
|
||||
* **animations:** ensure web-animations are caught within the Angular zone ([f80a157](https://github.com/angular/angular/commit/f80a157)), closes [#11881](https://github.com/angular/angular/issues/11881) [#11712](https://github.com/angular/angular/issues/11712) [#12355](https://github.com/angular/angular/issues/12355) [#11881](https://github.com/angular/angular/issues/11881) [#12546](https://github.com/angular/angular/issues/12546) [#12707](https://github.com/angular/angular/issues/12707) [#12774](https://github.com/angular/angular/issues/12774)
|
||||
* **common:** `NgSwitch` - don’t create the default case if another case matches ([#12726](https://github.com/angular/angular/issues/12726)) ([d8f23f4](https://github.com/angular/angular/commit/d8f23f4)), closes [#11297](https://github.com/angular/angular/issues/11297) [#9420](https://github.com/angular/angular/issues/9420)
|
||||
* **common:** I18nSelectPipe selects other case on default ([4708b24](https://github.com/angular/angular/commit/4708b24))
|
||||
* **common:** no TZ Offset added by DatePipe for dates without time ([#12380](https://github.com/angular/angular/issues/12380)) ([2aba8b0](https://github.com/angular/angular/commit/2aba8b0))
|
||||
* **common:** NgClass should throw a descriptive error when CSS class is not a string ([#12662](https://github.com/angular/angular/issues/12662)) ([f3793b5](https://github.com/angular/angular/commit/f3793b5)), closes [#12586](https://github.com/angular/angular/issues/12586)
|
||||
* **common:** DatePipe should handle empty string ([#12374](https://github.com/angular/angular/issues/12374)) ([3dc6177](https://github.com/angular/angular/commit/3dc6177))
|
||||
* **compiler:** don't convert undefined to null literals ([#11503](https://github.com/angular/angular/issues/11503)) ([f0cdb42](https://github.com/angular/angular/commit/f0cdb42)), closes [#11493](https://github.com/angular/angular/issues/11493)
|
||||
* **compiler:** generate safe access strictNullChecks compatible code ([#12800](https://github.com/angular/angular/issues/12800)) ([a965d11](https://github.com/angular/angular/commit/a965d11)), closes [#12795](https://github.com/angular/angular/issues/12795)
|
||||
* **compiler:** support more than 9 interpolations ([#12710](https://github.com/angular/angular/issues/12710)) ([22c021c](https://github.com/angular/angular/commit/22c021c)), closes [#10253](https://github.com/angular/angular/issues/10253)
|
||||
* **compiler:** use the other case by default in ICU messages ([55dc0e4](https://github.com/angular/angular/commit/55dc0e4))
|
||||
* **compiler-cli:** suppress closure compiler suspiciousCode check in codegen ([#12666](https://github.com/angular/angular/issues/12666)) ([7103754](https://github.com/angular/angular/commit/7103754))
|
||||
* **compiler-cli:** suppress two more closure compiler checks in codegen ([#12698](https://github.com/angular/angular/issues/12698)) ([77cbf7f](https://github.com/angular/angular/commit/77cbf7f))
|
||||
* **core:** allow to query content of templates that are stamped out at a different place ([f2bbef3](https://github.com/angular/angular/commit/f2bbef3)), closes [#12283](https://github.com/angular/angular/issues/12283) [#12094](https://github.com/angular/angular/issues/12094)
|
||||
* **core:** apply host attributes to root elements ([#12761](https://github.com/angular/angular/issues/12761)) ([ad3bf6c](https://github.com/angular/angular/commit/ad3bf6c)), closes [#12744](https://github.com/angular/angular/issues/12744)
|
||||
* **core:** ensure that component views that have no bindings recurse into nested components / view containers. ([051d748](https://github.com/angular/angular/commit/051d748))
|
||||
* **core:** fix pseudo-selector shimming ([#12754](https://github.com/angular/angular/issues/12754)) ([acbf1d8](https://github.com/angular/angular/commit/acbf1d8)), closes [#12730](https://github.com/angular/angular/issues/12730) [#12354](https://github.com/angular/angular/issues/12354)
|
||||
* **forms:** check if registerOnValidatorChange exists on validator before trying to invoke it ([#12801](https://github.com/angular/angular/issues/12801)) ([ef88147](https://github.com/angular/angular/commit/ef88147)), closes [#12593](https://github.com/angular/angular/issues/12593)
|
||||
* **forms:** getRawValue returns any instead of Object ([#12599](https://github.com/angular/angular/issues/12599)) ([09092ac](https://github.com/angular/angular/commit/09092ac))
|
||||
* **http:** preserve header case when copying headers ([#12697](https://github.com/angular/angular/issues/12697)) ([121e508](https://github.com/angular/angular/commit/121e508))
|
||||
* **router:** advance a route only after its children have been deactivated ([#12676](https://github.com/angular/angular/issues/12676)) ([9ddf9b3](https://github.com/angular/angular/commit/9ddf9b3)), closes [#11715](https://github.com/angular/angular/issues/11715)
|
||||
* **router:** avoid router initialization for non root components ([2a4bf9a](https://github.com/angular/angular/commit/2a4bf9a)), closes [#12338](https://github.com/angular/angular/issues/12338) [#12814](https://github.com/angular/angular/issues/12814)
|
||||
* **router:** check if windows.console exists before using it ([#12348](https://github.com/angular/angular/issues/12348)) ([7886561](https://github.com/angular/angular/commit/7886561))
|
||||
* **router:** correctly export concatMap operator in es5 ([#12430](https://github.com/angular/angular/issues/12430)) ([e25baa0](https://github.com/angular/angular/commit/e25baa0))
|
||||
* **router:** do not require the creation of empty-path routes when no url left ([2c11093](https://github.com/angular/angular/commit/2c11093)), closes [#12133](https://github.com/angular/angular/issues/12133)
|
||||
* **router:** ignore null or undefined query parameters ([#12333](https://github.com/angular/angular/issues/12333)) ([3052fb2](https://github.com/angular/angular/commit/3052fb2))
|
||||
* **router:** incorrect injector is used when instantiating components loaded lazily ([#12817](https://github.com/angular/angular/issues/12817)) ([52be848](https://github.com/angular/angular/commit/52be848))
|
||||
* **router:** resolve guard observables on the first emit ([#10412](https://github.com/angular/angular/issues/10412)) ([2e78b76](https://github.com/angular/angular/commit/2e78b76))
|
||||
* **router:** Route.isActive also compares query params ([#12321](https://github.com/angular/angular/issues/12321)) ([785b7b6](https://github.com/angular/angular/commit/785b7b6))
|
||||
* **router:** router should not swallow "unhandled" errors ([e5a753e](https://github.com/angular/angular/commit/e5a753e)), closes [#12802](https://github.com/angular/angular/issues/12802)
|
||||
* **router:** throw an error when encounter undefined route ([#12389](https://github.com/angular/angular/issues/12389)) ([77dc1ab](https://github.com/angular/angular/commit/77dc1ab))
|
||||
* **platform-browser:** enableDebugTools should create AngularTools by merging into context.ng ([#12003](https://github.com/angular/angular/issues/12003)) ([b2cf379](https://github.com/angular/angular/commit/b2cf379)), closes [#12002](https://github.com/angular/angular/issues/12002)
|
||||
* **platform-browser:** provide the ability to register global hammer.js events ([768cddb](https://github.com/angular/angular/commit/768cddb)), closes [#12797](https://github.com/angular/angular/issues/12797)
|
||||
* **tsc-wrapped:** harden collector against invalid asts ([#12793](https://github.com/angular/angular/issues/12793)) ([69f87ca](https://github.com/angular/angular/commit/69f87ca))
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0-rc.0"></a>
|
||||
# [2.2.0-rc.0](https://github.com/angular/angular/compare/2.2.0-beta.1...2.2.0-rc.0) (2016-11-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** dedupe NgModule declarations, … ([a178bc6](https://github.com/angular/angular/commit/a178bc6))
|
||||
* **compiler:** don’t double bind functions ([e391cac](https://github.com/angular/angular/commit/e391cac))
|
||||
* **compiler:** Don’t throw on empty property bindings ([642c1db](https://github.com/angular/angular/commit/642c1db)), closes [#12583](https://github.com/angular/angular/issues/12583)
|
||||
* **compiler:** support multiple components in a view container ([6fda972](https://github.com/angular/angular/commit/6fda972))
|
||||
* **core:** improve error when multiple components match the same element ([e9fd864](https://github.com/angular/angular/commit/e9fd864)), closes [#7067](https://github.com/angular/angular/issues/7067)
|
||||
* **router:** call data observers when the path changes ([1de04b2](https://github.com/angular/angular/commit/1de04b2))
|
||||
* **router:** CanDeactivate receives a wrong component ([830a780](https://github.com/angular/angular/commit/830a780)), closes [#12592](https://github.com/angular/angular/issues/12592)
|
||||
* **router:** rerun resolvers when url changes ([fe47e6b](https://github.com/angular/angular/commit/fe47e6b)), closes [#12603](https://github.com/angular/angular/issues/12603)
|
||||
* **router:** reset URL to the stable state when a navigation gets canceled ([d509ee0](https://github.com/angular/angular/commit/d509ee0)), closes [#10321](https://github.com/angular/angular/issues/10321)
|
||||
* **router:** routerLink should not prevent default on non-link elements ([8e221b8](https://github.com/angular/angular/commit/8e221b8))
|
||||
* **router:** run navigations serially ([091c390](https://github.com/angular/angular/commit/091c390)), closes [#11754](https://github.com/angular/angular/issues/11754)
|
||||
* **upgrade:** silent bootstrap failures ([fa93fd6](https://github.com/angular/angular/commit/fa93fd6)), closes [#12062](https://github.com/angular/angular/issues/12062)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add the find method to QueryList ([7c16ef9](https://github.com/angular/angular/commit/7c16ef9))
|
||||
|
||||
|
||||
|
||||
<a name="2.1.2"></a>
|
||||
# [2.1.2](https://github.com/angular/angular/compare/2.1.1...2.1.2) (2016-10-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** don't access view local variables nor pipes in host expressions ([#12396](https://github.com/angular/angular/issues/12396)) ([867494a](https://github.com/angular/angular/commit/867494a)), closes [#12004](https://github.com/angular/angular/issues/12004) [#12071](https://github.com/angular/angular/issues/12071)
|
||||
@ -18,6 +181,10 @@
|
||||
* **router:** preserve resolve data ([6ccbfd4](https://github.com/angular/angular/commit/6ccbfd4)), closes [#12306](https://github.com/angular/angular/issues/12306)
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0-beta.1"></a>
|
||||
# [2.2.0-beta.1](https://github.com/angular/angular/compare/2.2.0-beta.0...2.2.0-beta.1) (2016-10-27)
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **upgrade:** re-export the new static upgrade APIs on new entry ([a26dd28](https://github.com/angular/angular/commit/a26dd28))
|
||||
@ -28,13 +195,16 @@
|
||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
### BREAKING CHANGES (only for beta version users)
|
||||
|
||||
* upgrade: Four newly added APIs in 2.2.0-beta:
|
||||
downgradeComponent, downgradeInjectable, UpgradeComponent, and UpgradeModule are no longer exported by @angular/upgrade.
|
||||
Import these from @angular/upgrade/static instead.
|
||||
|
||||
|
||||
Note: The 2.2.0-beta.1 release also contains all the changes present in the 2.1.2 release.
|
||||
|
||||
|
||||
|
||||
# [2.1.1](https://github.com/angular/angular/compare/2.1.0...2.1.1) (2016-10-20)
|
||||
|
||||
|
@ -17,7 +17,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
||||
|
||||
## <a name="question"></a> Got a Question or Problem?
|
||||
|
||||
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](https://stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||
|
||||
StackOverflow is a much better place to ask questions since:
|
||||
|
||||
|
63
build.sh
63
build.sh
@ -14,12 +14,16 @@ PACKAGES=(core
|
||||
platform-webworker
|
||||
platform-webworker-dynamic
|
||||
http
|
||||
router
|
||||
upgrade
|
||||
router
|
||||
compiler-cli
|
||||
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 +35,10 @@ for ARG in "$@"; do
|
||||
--bundle=*)
|
||||
BUNDLE=( "${ARG#--bundle=}" )
|
||||
;;
|
||||
--publish)
|
||||
VERSION_SUFFIX=""
|
||||
REMOVE_BENCHPRESS=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $ARG."
|
||||
exit 1
|
||||
@ -38,6 +46,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
|
||||
@ -63,10 +75,11 @@ if [[ ${BUILD_ALL} == true ]]; then
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/base64-js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
ln -s ../../../../node_modules/hammerjs/hammer.js .
|
||||
cd -
|
||||
|
||||
echo "====== Copying files needed for benchmarks ====="
|
||||
@ -78,7 +91,6 @@ if [[ ${BUILD_ALL} == true ]]; then
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
@ -102,15 +114,28 @@ do
|
||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
||||
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
||||
UMD_UPGRADE_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.js
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
UMD_STATIC_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.min.js
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||
UMD_UPGRADE_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.min.js
|
||||
|
||||
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}
|
||||
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||
|
||||
if [[ -e ${SRCDIR}/tsconfig-upgrade.json ]]; then
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-upgrade.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-upgrade.json
|
||||
fi
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||
|
||||
@ -171,9 +196,37 @@ do
|
||||
mv ${UMD_STATIC_ES5_PATH}.tmp ${UMD_STATIC_ES5_PATH}
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_STATIC_ES5_MIN_PATH} ${UMD_STATIC_ES5_PATH}
|
||||
fi
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
if [[ -e rollup-upgrade.config.js ]]; then
|
||||
echo "====== Rollup ${PACKAGE} upgrade"
|
||||
../../../node_modules/.bin/rollup -c rollup-upgrade.config.js
|
||||
# create dir because it doesn't exist yet, we should move the src code here and remove this line
|
||||
mkdir ${DESTDIR}/upgrade
|
||||
echo "{\"main\": \"../bundles/${PACKAGE}-upgrade.umd.js\"}" > ${DESTDIR}/upgrade/package.json
|
||||
cat ${LICENSE_BANNER} > ${UMD_UPGRADE_ES5_PATH}.tmp
|
||||
cat ${UMD_UPGRADE_ES5_PATH} >> ${UMD_UPGRADE_ES5_PATH}.tmp
|
||||
mv ${UMD_UPGRADE_ES5_PATH}.tmp ${UMD_UPGRADE_ES5_PATH}
|
||||
$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:
|
||||
|
31
gulpfile.js
31
gulpfile.js
@ -125,38 +125,31 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||
.on('close', done);
|
||||
});
|
||||
|
||||
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
|
||||
// focused tests is found.
|
||||
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded
|
||||
// tests in our code base.
|
||||
gulp.task('check-tests', function() {
|
||||
const ddescribeIit = require('gulp-ddescribe-iit');
|
||||
return gulp
|
||||
.src([
|
||||
'modules/**/*.spec.ts',
|
||||
'modules/**/*_spec.ts',
|
||||
])
|
||||
.pipe(ddescribeIit({allowDisabledTests: true}));
|
||||
});
|
||||
|
||||
// Check the coding standards and programming errors
|
||||
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||
const tslint = require('gulp-tslint');
|
||||
// Built-in rules are at
|
||||
// https://github.com/palantir/tslint#supported-rules
|
||||
// https://palantir.github.io/tslint/rules/
|
||||
const tslintConfig = require('./tslint.json');
|
||||
return gulp
|
||||
.src([
|
||||
// todo(vicb): add .js files when supported
|
||||
// see https://github.com/palantir/tslint/pull/1515
|
||||
'modules/@angular/**/*.ts',
|
||||
'modules/benchpress/**/*.ts',
|
||||
'./modules/**/*.ts',
|
||||
'./tools/**/*.ts',
|
||||
'./*.ts',
|
||||
|
||||
// Ignore TypeScript mocks because it's not managed by us
|
||||
'!./tools/@angular/tsc-wrapped/test/typescript.mocks.ts',
|
||||
|
||||
// Ignore generated files due to lack of copyright header
|
||||
// todo(alfaproject): make generated files lintable
|
||||
'!**/*.d.ts',
|
||||
'!**/*.ngfactory.ts',
|
||||
])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
rulesDirectory: 'dist/tools/tslint',
|
||||
formatter: 'prose',
|
||||
}))
|
||||
.pipe(tslint.report({emitError: true}));
|
||||
|
@ -50,6 +50,7 @@ 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/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
@ -90,17 +91,17 @@ module.exports = function(config) {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 3,
|
||||
timeout: 600,
|
||||
timeout: 1800,
|
||||
pollingTimeout: 10000,
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout: 60000,
|
||||
captureTimeout: 180000,
|
||||
browserDisconnectTimeout: 180000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserNoActivityTimeout: 60000,
|
||||
browserNoActivityTimeout: 300000,
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
|
@ -10,7 +10,7 @@ declare var exportFunction: any;
|
||||
declare var unsafeWindow: any;
|
||||
|
||||
exportFunction(function() {
|
||||
var curTime = unsafeWindow.performance.now();
|
||||
const curTime = unsafeWindow.performance.now();
|
||||
(<any>self).port.emit('startProfiler', curTime);
|
||||
}, unsafeWindow, {defineAs: 'startProfiler'});
|
||||
|
||||
@ -28,11 +28,11 @@ exportFunction(function() {
|
||||
}, unsafeWindow, {defineAs: 'forceGC'});
|
||||
|
||||
exportFunction(function(name: string) {
|
||||
var curTime = unsafeWindow.performance.now();
|
||||
const curTime = unsafeWindow.performance.now();
|
||||
(<any>self).port.emit('markStart', name, curTime);
|
||||
}, unsafeWindow, {defineAs: 'markStart'});
|
||||
|
||||
exportFunction(function(name: string) {
|
||||
var curTime = unsafeWindow.performance.now();
|
||||
const curTime = unsafeWindow.performance.now();
|
||||
(<any>self).port.emit('markEnd', name, curTime);
|
||||
}, unsafeWindow, {defineAs: 'markEnd'});
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var {Cc, Ci, Cu} = require('chrome');
|
||||
var os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||
var ParserUtil = require('./parser_util');
|
||||
const {Cc, Ci, Cu} = require('chrome');
|
||||
const os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||
const ParserUtil = require('./parser_util');
|
||||
|
||||
class Profiler {
|
||||
private _profiler: any;
|
||||
@ -26,8 +26,8 @@ class Profiler {
|
||||
stop() { this._profiler.StopProfiler(); }
|
||||
|
||||
getProfilePerfEvents() {
|
||||
var profileData = this._profiler.getProfileData();
|
||||
var perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
||||
const profileData = this._profiler.getProfileData();
|
||||
let perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
||||
perfEvents = this._mergeMarkerEvents(perfEvents);
|
||||
perfEvents.sort(function(event1: any, event2: any) {
|
||||
return event1.ts - event2.ts;
|
||||
@ -55,9 +55,9 @@ function forceGC() {
|
||||
os.notifyObservers(null, 'child-gc-request', null);
|
||||
};
|
||||
|
||||
var mod = require('sdk/page-mod');
|
||||
var data = require('sdk/self').data;
|
||||
var profiler = new Profiler();
|
||||
const mod = require('sdk/page-mod');
|
||||
const data = require('sdk/self').data;
|
||||
const profiler = new Profiler();
|
||||
mod.PageMod({
|
||||
include: ['*'],
|
||||
contentScriptFile: data.url('installed_script.js'),
|
||||
|
@ -12,11 +12,11 @@
|
||||
* within the perf profile.
|
||||
*/
|
||||
export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
var inProgressEvents = new Map(); // map from event name to start time
|
||||
var finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
||||
var addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
||||
var categorizedEventName = categorizeEvent(eventName);
|
||||
var args: {[key: string]: any} = undefined;
|
||||
const inProgressEvents = new Map(); // map from event name to start time
|
||||
const finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
||||
const addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
||||
const categorizedEventName = categorizeEvent(eventName);
|
||||
let args: {[key: string]: any} = undefined;
|
||||
if (categorizedEventName == 'gc') {
|
||||
// TODO: We cannot measure heap size at the moment
|
||||
args = {usedHeapSize: 0};
|
||||
@ -31,17 +31,17 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
}
|
||||
};
|
||||
|
||||
var samples = perfProfile.threads[0].samples;
|
||||
const samples = perfProfile.threads[0].samples;
|
||||
// In perf profile, firefox samples all the frames in set time intervals. Here
|
||||
// we go through all the samples and construct the start and end time for each
|
||||
// event.
|
||||
for (var i = 0; i < samples.length; ++i) {
|
||||
var sample = samples[i];
|
||||
var sampleTime = sample.time;
|
||||
for (let i = 0; i < samples.length; ++i) {
|
||||
const sample = samples[i];
|
||||
const sampleTime = sample.time;
|
||||
|
||||
// Add all the frames into a set so it's easier/faster to find the set
|
||||
// differences
|
||||
var sampleFrames = new Set();
|
||||
const sampleFrames = new Set();
|
||||
sample.frames.forEach(function(frame: {[key: string]: any}) {
|
||||
sampleFrames.add(frame['location']);
|
||||
});
|
||||
@ -49,7 +49,7 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
// If an event is in the inProgressEvents map, but not in the current sample,
|
||||
// then it must have just finished. We add this event to the finishedEvents
|
||||
// array and remove it from the inProgressEvents map.
|
||||
var previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
|
||||
const previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
|
||||
inProgressEvents.forEach(function(startTime, eventName) {
|
||||
if (!(sampleFrames.has(eventName))) {
|
||||
addFinishedEvent(eventName, startTime, previousSampleTime);
|
||||
@ -69,7 +69,7 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
|
||||
// If anything is still in progress, we need to included it as a finished event
|
||||
// since recording ended.
|
||||
var lastSampleTime = samples[samples.length - 1].time;
|
||||
const lastSampleTime = samples[samples.length - 1].time;
|
||||
inProgressEvents.forEach(function(startTime, eventName) {
|
||||
addFinishedEvent(eventName, startTime, lastSampleTime);
|
||||
});
|
||||
|
@ -6,15 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var q = require('q');
|
||||
var FirefoxProfile = require('firefox-profile');
|
||||
var jpm = require('jpm/lib/xpi');
|
||||
var pathUtil = require('path');
|
||||
const q = require('q');
|
||||
const FirefoxProfile = require('firefox-profile');
|
||||
const jpm = require('jpm/lib/xpi');
|
||||
const pathUtil = require('path');
|
||||
|
||||
var PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
||||
const PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
||||
|
||||
exports.getAbsolutePath = function(path: string) {
|
||||
var normalizedPath = pathUtil.normalize(path);
|
||||
const normalizedPath = pathUtil.normalize(path);
|
||||
if (pathUtil.resolve(normalizedPath) == normalizedPath) {
|
||||
// Already absolute path
|
||||
return normalizedPath;
|
||||
@ -24,12 +24,12 @@ exports.getAbsolutePath = function(path: string) {
|
||||
};
|
||||
|
||||
exports.getFirefoxProfile = function(extensionPath: string) {
|
||||
var deferred = q.defer();
|
||||
const deferred = q.defer();
|
||||
|
||||
var firefoxProfile = new FirefoxProfile();
|
||||
const firefoxProfile = new FirefoxProfile();
|
||||
firefoxProfile.addExtensions([extensionPath], () => {
|
||||
firefoxProfile.encoded((encodedProfile: any) => {
|
||||
var multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||
const multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||
deferred.resolve(multiCapabilities);
|
||||
});
|
||||
});
|
||||
@ -38,10 +38,10 @@ exports.getFirefoxProfile = function(extensionPath: string) {
|
||||
};
|
||||
|
||||
exports.getFirefoxProfileWithExtension = function() {
|
||||
var absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
||||
var packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
||||
const absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
||||
const packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
||||
|
||||
var savedCwd = process.cwd();
|
||||
const savedCwd = process.cwd();
|
||||
process.chdir(absPackageJsonDir);
|
||||
|
||||
return jpm(packageJson).then((xpiPath: string) => {
|
||||
|
@ -55,9 +55,9 @@ export class MultiMetric extends Metric {
|
||||
}
|
||||
|
||||
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
||||
var result: {[key: string]: string} = {};
|
||||
const result: {[key: string]: string} = {};
|
||||
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
||||
return result;
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
||||
const _CHILDREN = new OpaqueToken('MultiMetric.children');
|
||||
|
@ -56,7 +56,7 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
|
||||
describe(): {[key: string]: string} {
|
||||
var res: {[key: string]: any} = {
|
||||
const res: {[key: string]: any} = {
|
||||
'scriptTime': 'script execution time in ms, including gc and render',
|
||||
'pureScriptTime': 'script execution time in ms, without gc nor render'
|
||||
};
|
||||
@ -80,7 +80,7 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
if (this._captureFrames) {
|
||||
if (!this._perfLogFeatures.frameCapture) {
|
||||
var warningMsg = 'WARNING: Metric requested, but not supported by driver';
|
||||
const warningMsg = 'WARNING: Metric requested, but not supported by driver';
|
||||
// using dot syntax for metric name to keep them grouped together in console reporter
|
||||
res['frameTime.mean'] = warningMsg;
|
||||
res['frameTime.worst'] = warningMsg;
|
||||
@ -93,14 +93,14 @@ export class PerflogMetric extends Metric {
|
||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
||||
}
|
||||
}
|
||||
for (let name in this._microMetrics) {
|
||||
for (const name in this._microMetrics) {
|
||||
res[name] = this._microMetrics[name];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
beginMeasure(): Promise<any> {
|
||||
var resultPromise = Promise.resolve(null);
|
||||
let resultPromise = Promise.resolve(null);
|
||||
if (this._forceGc) {
|
||||
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
||||
}
|
||||
@ -119,7 +119,7 @@ export class PerflogMetric extends Metric {
|
||||
private _endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
||||
return this._endMeasure(true).then((measureValues) => {
|
||||
// disable frame capture for measurements during forced gc
|
||||
var originalFrameCaptureValue = this._captureFrames;
|
||||
const originalFrameCaptureValue = this._captureFrames;
|
||||
this._captureFrames = false;
|
||||
return this._driverExtension.gc()
|
||||
.then((_) => this._endMeasure(restartMeasure))
|
||||
@ -137,8 +137,8 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
|
||||
private _endMeasure(restart: boolean): Promise<{[key: string]: number}> {
|
||||
var markName = this._markName(this._measureCount - 1);
|
||||
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||
const markName = this._markName(this._measureCount - 1);
|
||||
const nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||
return this._driverExtension.timeEnd(markName, nextMarkName)
|
||||
.then((_) => this._readUntilEndMark(markName));
|
||||
}
|
||||
@ -150,26 +150,26 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
return this._driverExtension.readPerfLog().then((events) => {
|
||||
this._addEvents(events);
|
||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||
const result = this._aggregateEvents(this._remainingEvents, markName);
|
||||
if (result) {
|
||||
this._remainingEvents = events;
|
||||
return result;
|
||||
}
|
||||
var resolve: (result: any) => void;
|
||||
var promise = new Promise(res => { resolve = res; });
|
||||
let resolve: (result: any) => void;
|
||||
const promise = new Promise(res => { resolve = res; });
|
||||
this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
private _addEvents(events: PerfLogEvent[]) {
|
||||
var needSort = false;
|
||||
let needSort = false;
|
||||
events.forEach(event => {
|
||||
if (event['ph'] === 'X') {
|
||||
needSort = true;
|
||||
var startEvent: PerfLogEvent = {};
|
||||
var endEvent: PerfLogEvent = {};
|
||||
for (let prop in event) {
|
||||
const startEvent: PerfLogEvent = {};
|
||||
const endEvent: PerfLogEvent = {};
|
||||
for (const prop in event) {
|
||||
startEvent[prop] = event[prop];
|
||||
endEvent[prop] = event[prop];
|
||||
}
|
||||
@ -185,14 +185,14 @@ export class PerflogMetric extends Metric {
|
||||
if (needSort) {
|
||||
// Need to sort because of the ph==='X' events
|
||||
this._remainingEvents.sort((a, b) => {
|
||||
var diff = a['ts'] - b['ts'];
|
||||
const diff = a['ts'] - b['ts'];
|
||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _aggregateEvents(events: PerfLogEvent[], markName: string): {[key: string]: number} {
|
||||
var result: {[key: string]: number} = {'scriptTime': 0, 'pureScriptTime': 0};
|
||||
const result: {[key: string]: number} = {'scriptTime': 0, 'pureScriptTime': 0};
|
||||
if (this._perfLogFeatures.gc) {
|
||||
result['gcTime'] = 0;
|
||||
result['majorGcTime'] = 0;
|
||||
@ -207,7 +207,7 @@ export class PerflogMetric extends Metric {
|
||||
result['frameTime.worst'] = 0;
|
||||
result['frameTime.smooth'] = 0;
|
||||
}
|
||||
for (let name in this._microMetrics) {
|
||||
for (const name in this._microMetrics) {
|
||||
result[name] = 0;
|
||||
}
|
||||
if (this._receivedData) {
|
||||
@ -217,11 +217,11 @@ export class PerflogMetric extends Metric {
|
||||
result['requestCount'] = 0;
|
||||
}
|
||||
|
||||
var markStartEvent: PerfLogEvent = null;
|
||||
var markEndEvent: PerfLogEvent = null;
|
||||
let markStartEvent: PerfLogEvent = null;
|
||||
let markEndEvent: PerfLogEvent = null;
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
const ph = event['ph'];
|
||||
const name = event['name'];
|
||||
if (ph === 'B' && name === markName) {
|
||||
markStartEvent = event;
|
||||
} else if (ph === 'I' && name === 'navigationStart') {
|
||||
@ -237,23 +237,23 @@ export class PerflogMetric extends Metric {
|
||||
return null;
|
||||
}
|
||||
|
||||
var gcTimeInScript = 0;
|
||||
var renderTimeInScript = 0;
|
||||
let gcTimeInScript = 0;
|
||||
let renderTimeInScript = 0;
|
||||
|
||||
var frameTimestamps: number[] = [];
|
||||
var frameTimes: number[] = [];
|
||||
var frameCaptureStartEvent: PerfLogEvent = null;
|
||||
var frameCaptureEndEvent: PerfLogEvent = null;
|
||||
const frameTimestamps: number[] = [];
|
||||
const frameTimes: number[] = [];
|
||||
let frameCaptureStartEvent: PerfLogEvent = null;
|
||||
let frameCaptureEndEvent: PerfLogEvent = null;
|
||||
|
||||
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||
var intervalStartCount: {[key: string]: number} = {};
|
||||
const intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||
const intervalStartCount: {[key: string]: number} = {};
|
||||
|
||||
var inMeasureRange = false;
|
||||
let inMeasureRange = false;
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
var microIterations = 1;
|
||||
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||
const ph = event['ph'];
|
||||
let name = event['name'];
|
||||
let microIterations = 1;
|
||||
const microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||
if (microIterationsMatch) {
|
||||
name = microIterationsMatch[1];
|
||||
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||
@ -307,15 +307,15 @@ export class PerflogMetric extends Metric {
|
||||
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||
intervalStartCount[name]--;
|
||||
if (intervalStartCount[name] === 0) {
|
||||
var startEvent = intervalStarts[name];
|
||||
var duration = (event['ts'] - startEvent['ts']);
|
||||
const startEvent = intervalStarts[name];
|
||||
const duration = (event['ts'] - startEvent['ts']);
|
||||
intervalStarts[name] = null;
|
||||
if (name === 'gc') {
|
||||
result['gcTime'] += duration;
|
||||
var amount =
|
||||
const amount =
|
||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||
result['gcAmount'] += amount;
|
||||
var majorGc = event['args']['majorGc'];
|
||||
const majorGc = event['args']['majorGc'];
|
||||
if (majorGc && majorGc) {
|
||||
result['majorGcTime'] += duration;
|
||||
}
|
||||
@ -351,7 +351,7 @@ export class PerflogMetric extends Metric {
|
||||
|
||||
private _addFrameMetrics(result: {[key: string]: number}, frameTimes: any[]) {
|
||||
result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
|
||||
var firstFrame = frameTimes[0];
|
||||
const firstFrame = frameTimes[0];
|
||||
result['frameTime.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
|
||||
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
|
||||
result['frameTime.smooth'] =
|
||||
@ -361,11 +361,11 @@ export class PerflogMetric extends Metric {
|
||||
private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; }
|
||||
}
|
||||
|
||||
var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
||||
const _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
||||
|
||||
var _MAX_RETRY_COUNT = 20;
|
||||
var _MARK_NAME_PREFIX = 'benchpress';
|
||||
const _MAX_RETRY_COUNT = 20;
|
||||
const _MARK_NAME_PREFIX = 'benchpress';
|
||||
|
||||
var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
||||
const _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
||||
// using 17ms as a somewhat looser threshold, instead of 16.6666ms
|
||||
var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
||||
const _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
||||
|
@ -33,12 +33,12 @@ export class UserMetric extends Metric {
|
||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||
let resolve: (result: any) => void;
|
||||
let reject: (error: any) => void;
|
||||
let promise = new Promise((res, rej) => {
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
let adapter = this._wdAdapter;
|
||||
let names = Object.keys(this._userMetrics);
|
||||
const adapter = this._wdAdapter;
|
||||
const names = Object.keys(this._userMetrics);
|
||||
|
||||
function getAndClearValues() {
|
||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
||||
@ -46,7 +46,7 @@ export class UserMetric extends Metric {
|
||||
if (values.every(v => typeof v === 'number')) {
|
||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||
.then((_: any[]) => {
|
||||
let map: {[k: string]: any} = {};
|
||||
const map: {[k: string]: any} = {};
|
||||
for (let i = 0, n = names.length; i < n; i++) {
|
||||
map[names[i]] = values[i];
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ export class ConsoleReporter extends Reporter {
|
||||
];
|
||||
|
||||
private static _lpad(value: string, columnWidth: number, fill = ' ') {
|
||||
var result = '';
|
||||
for (var i = 0; i < columnWidth - value.length; i++) {
|
||||
let result = '';
|
||||
for (let i = 0; i < columnWidth - value.length; i++) {
|
||||
result += fill;
|
||||
}
|
||||
return result + value;
|
||||
@ -49,7 +49,7 @@ export class ConsoleReporter extends Reporter {
|
||||
private _printDescription(sampleDescription: SampleDescription) {
|
||||
this._print(`BENCHMARK ${sampleDescription.id}`);
|
||||
this._print('Description:');
|
||||
var props = sortedProps(sampleDescription.description);
|
||||
const props = sortedProps(sampleDescription.description);
|
||||
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
||||
this._print('Metrics:');
|
||||
this._metricNames.forEach((metricName) => {
|
||||
@ -61,8 +61,8 @@ export class ConsoleReporter extends Reporter {
|
||||
}
|
||||
|
||||
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
||||
var formattedValues = this._metricNames.map(metricName => {
|
||||
var value = measureValues.values[metricName];
|
||||
const formattedValues = this._metricNames.map(metricName => {
|
||||
const value = measureValues.values[metricName];
|
||||
return formatNum(value);
|
||||
});
|
||||
this._printStringRow(formattedValues);
|
||||
|
@ -38,7 +38,7 @@ export class JsonFileReporter extends Reporter {
|
||||
sortedProps(this._description.metrics).forEach((metricName) => {
|
||||
stats[metricName] = formatStats(validSample, metricName);
|
||||
});
|
||||
var content = JSON.stringify(
|
||||
const content = JSON.stringify(
|
||||
{
|
||||
'description': this._description,
|
||||
'stats': stats,
|
||||
@ -46,7 +46,7 @@ export class JsonFileReporter extends Reporter {
|
||||
'validSample': validSample,
|
||||
},
|
||||
null, 2);
|
||||
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||
const filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||
return this._writeFile(filePath, content);
|
||||
}
|
||||
}
|
||||
|
@ -39,4 +39,4 @@ export class MultiReporter extends Reporter {
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiReporter.children');
|
||||
const _CHILDREN = new OpaqueToken('MultiReporter.children');
|
||||
|
@ -18,10 +18,10 @@ export function sortedProps(obj: {[key: string]: any}) {
|
||||
}
|
||||
|
||||
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
||||
var samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
||||
var mean = Statistic.calculateMean(samples);
|
||||
var cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
||||
var formattedMean = formatNum(mean);
|
||||
const samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
||||
const mean = Statistic.calculateMean(samples);
|
||||
const cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
||||
const formattedMean = formatNum(mean);
|
||||
// Note: Don't use the unicode character for +- as it might cause
|
||||
// hickups for consoles...
|
||||
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||
|
@ -45,7 +45,7 @@ export class Runner {
|
||||
providers?: Provider[],
|
||||
userMetrics?: {[key: string]: string}
|
||||
}): Promise<SampleState> {
|
||||
var sampleProviders: Provider[] = [
|
||||
const sampleProviders: Provider[] = [
|
||||
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
||||
{provide: Options.EXECUTE, useValue: execute}
|
||||
];
|
||||
@ -62,33 +62,33 @@ export class Runner {
|
||||
sampleProviders.push(providers);
|
||||
}
|
||||
|
||||
var inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||
var adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||
const inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||
const adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||
|
||||
return Promise
|
||||
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
||||
.then((args) => {
|
||||
var capabilities = args[0];
|
||||
var userAgent = args[1];
|
||||
const capabilities = args[0];
|
||||
const userAgent = args[1];
|
||||
|
||||
// This might still create instances twice. We are creating a new injector with all the
|
||||
// providers.
|
||||
// Only WebDriverAdapter is reused.
|
||||
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
||||
// injectors are handled better.
|
||||
var injector = ReflectiveInjector.resolveAndCreate([
|
||||
const injector = ReflectiveInjector.resolveAndCreate([
|
||||
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
||||
{provide: Options.USER_AGENT, useValue: userAgent},
|
||||
{provide: WebDriverAdapter, useValue: adapter}
|
||||
]);
|
||||
|
||||
var sampler = injector.get(Sampler);
|
||||
const sampler = injector.get(Sampler);
|
||||
return sampler.sample();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var _DEFAULT_PROVIDERS = [
|
||||
const _DEFAULT_PROVIDERS = [
|
||||
Options.DEFAULT_PROVIDERS,
|
||||
Sampler.PROVIDERS,
|
||||
ConsoleReporter.PROVIDERS,
|
||||
|
@ -49,7 +49,7 @@ export class Sampler {
|
||||
}
|
||||
|
||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||
var resultPromise: Promise<SampleState>;
|
||||
let resultPromise: Promise<SampleState>;
|
||||
if (this._prepare !== Options.NO_PREPARE) {
|
||||
resultPromise = this._driver.waitFor(this._prepare);
|
||||
} else {
|
||||
@ -64,10 +64,10 @@ export class Sampler {
|
||||
}
|
||||
|
||||
private _report(state: SampleState, metricValues: {[key: string]: any}): Promise<SampleState> {
|
||||
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||
var completeSample = state.completeSample.concat([measureValues]);
|
||||
var validSample = this._validator.validate(completeSample);
|
||||
var resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||
const measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||
const completeSample = state.completeSample.concat([measureValues]);
|
||||
const validSample = this._validator.validate(completeSample);
|
||||
let resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||
if (isPresent(validSample)) {
|
||||
resultPromise =
|
||||
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample));
|
||||
|
@ -12,14 +12,14 @@ export class Statistic {
|
||||
}
|
||||
|
||||
static calculateMean(samples: number[]) {
|
||||
var total = 0;
|
||||
let total = 0;
|
||||
// TODO: use reduce
|
||||
samples.forEach(x => total += x);
|
||||
return total / samples.length;
|
||||
}
|
||||
|
||||
static calculateStandardDeviation(samples: number[], mean: number) {
|
||||
var deviation = 0;
|
||||
let deviation = 0;
|
||||
// TODO: use reduce
|
||||
samples.forEach(x => deviation += Math.pow(x - mean, 2));
|
||||
deviation = deviation / (samples.length);
|
||||
@ -30,9 +30,9 @@ export class Statistic {
|
||||
static calculateRegressionSlope(
|
||||
xValues: number[], xMean: number, yValues: number[], yMean: number) {
|
||||
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
||||
var dividendSum = 0;
|
||||
var divisorSum = 0;
|
||||
for (var i = 0; i < xValues.length; i++) {
|
||||
let dividendSum = 0;
|
||||
let divisorSum = 0;
|
||||
for (let i = 0; i < xValues.length; i++) {
|
||||
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
||||
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ export type PerfLogEvent = {
|
||||
*/
|
||||
export abstract class WebDriverExtension {
|
||||
static provideFirstSupported(childTokens: any[]): any[] {
|
||||
var res = [
|
||||
const res = [
|
||||
{
|
||||
provide: _CHILDREN,
|
||||
useFactory: (injector: Injector) => childTokens.map(token => injector.get(token)),
|
||||
@ -43,7 +43,7 @@ export abstract class WebDriverExtension {
|
||||
{
|
||||
provide: WebDriverExtension,
|
||||
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
||||
var delegate: WebDriverExtension;
|
||||
let delegate: WebDriverExtension;
|
||||
children.forEach(extension => {
|
||||
if (extension.supports(capabilities)) {
|
||||
delegate = extension;
|
||||
@ -101,4 +101,4 @@ export class PerfLogFeatures {
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
||||
const _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
||||
|
@ -34,7 +34,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
if (!userAgent) {
|
||||
return -1;
|
||||
}
|
||||
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||
let v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||
if (!v) {
|
||||
return -1;
|
||||
}
|
||||
@ -52,7 +52,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
let script = `console.timeEnd('${name}');`;
|
||||
if (restartName) {
|
||||
script += `console.time('${restartName}');`;
|
||||
}
|
||||
@ -67,9 +67,9 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
return this._driver.executeScript('1+1')
|
||||
.then((_) => this._driver.logs('performance'))
|
||||
.then((entries) => {
|
||||
var events: PerfLogEvent[] = [];
|
||||
const events: PerfLogEvent[] = [];
|
||||
entries.forEach(entry => {
|
||||
var message = JSON.parse(entry['message'])['message'];
|
||||
const message = JSON.parse(entry['message'])['message'];
|
||||
if (message['method'] === 'Tracing.dataCollected') {
|
||||
events.push(message['params']);
|
||||
}
|
||||
@ -95,8 +95,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||
var name = event['name'];
|
||||
var args = event['args'];
|
||||
const name = event['name'];
|
||||
const args = event['args'];
|
||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||
return normalizeEvent(event, {'name': name});
|
||||
} else if (this._isEvent(
|
||||
@ -109,7 +109,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
// new surfaces framework (not broadly enabled yet)
|
||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||
// always available if something is rendered
|
||||
var frameCount = event['args']['data']['frame_count'];
|
||||
const frameCount = event['args']['data']['frame_count'];
|
||||
if (frameCount > 1) {
|
||||
throw new Error('multi-frame render stats not supported');
|
||||
}
|
||||
@ -122,14 +122,14 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||
return normalizeEvent(event, {'name': 'render'});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||
var normArgs = {
|
||||
const normArgs = {
|
||||
'majorGc': true,
|
||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
};
|
||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||
var normArgs = {
|
||||
const normArgs = {
|
||||
'majorGc': false,
|
||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
@ -151,11 +151,11 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
|
||||
return normalizeEvent(event, {'name': 'render'});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
|
||||
let normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
|
||||
const normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
|
||||
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
|
||||
let data = args['data'];
|
||||
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||
const data = args['data'];
|
||||
const normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||
@ -168,7 +168,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
private _isEvent(
|
||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||
expectedName: string = null): boolean {
|
||||
var hasCategories = expectedCategories.reduce(
|
||||
const hasCategories = expectedCategories.reduce(
|
||||
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||
}
|
||||
@ -183,7 +183,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||
var ph = chromeEvent['ph'].toUpperCase();
|
||||
let ph = chromeEvent['ph'].toUpperCase();
|
||||
if (ph === 'S') {
|
||||
ph = 'B';
|
||||
} else if (ph === 'F') {
|
||||
@ -192,16 +192,16 @@ function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent):
|
||||
// mark events from navigation timing
|
||||
ph = 'I';
|
||||
}
|
||||
var result: {[key: string]: any} =
|
||||
const result: {[key: string]: any} =
|
||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||
if (ph === 'X') {
|
||||
var dur = chromeEvent['dur'];
|
||||
let dur = chromeEvent['dur'];
|
||||
if (dur === undefined) {
|
||||
dur = chromeEvent['tdur'];
|
||||
}
|
||||
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||
}
|
||||
for (let prop in data) {
|
||||
for (const prop in data) {
|
||||
result[prop] = data[prop];
|
||||
}
|
||||
return result;
|
||||
|
@ -34,7 +34,7 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = 'window.markEnd("' + name + '");';
|
||||
let script = 'window.markEnd("' + name + '");';
|
||||
if (isPresent(restartName)) {
|
||||
script += 'window.markStart("' + restartName + '");';
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
let script = `console.timeEnd('${name}');`;
|
||||
if (isPresent(restartName)) {
|
||||
script += `console.time('${restartName}');`;
|
||||
}
|
||||
@ -39,9 +39,9 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
return this._driver.executeScript('1+1')
|
||||
.then((_) => this._driver.logs('performance'))
|
||||
.then((entries) => {
|
||||
var records: any[] = [];
|
||||
const records: any[] = [];
|
||||
entries.forEach(entry => {
|
||||
var message = JSON.parse(entry['message'])['message'];
|
||||
const message = JSON.parse(entry['message'])['message'];
|
||||
if (message['method'] === 'Timeline.eventRecorded') {
|
||||
records.push(message['params']['record']);
|
||||
}
|
||||
@ -56,11 +56,11 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
events = [];
|
||||
}
|
||||
records.forEach((record) => {
|
||||
var endEvent: PerfLogEvent = null;
|
||||
var type = record['type'];
|
||||
var data = record['data'];
|
||||
var startTime = record['startTime'];
|
||||
var endTime = record['endTime'];
|
||||
let endEvent: PerfLogEvent = null;
|
||||
const type = record['type'];
|
||||
const data = record['data'];
|
||||
const startTime = record['startTime'];
|
||||
const endTime = record['endTime'];
|
||||
|
||||
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
|
||||
events.push(createStartEvent('script', startTime));
|
||||
@ -95,7 +95,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
|
||||
function createEvent(
|
||||
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||
var result: PerfLogEvent = {
|
||||
const result: PerfLogEvent = {
|
||||
'cat': 'timeline',
|
||||
'name': name,
|
||||
'ts': time,
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
require('core-js');
|
||||
require('reflect-metadata');
|
||||
var testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
||||
const testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
||||
|
||||
exports.config = {
|
||||
specs: ['spec.js', 'sample_benchmark.js'],
|
||||
|
@ -10,10 +10,10 @@ import {convertPerfProfileToEvents} from '../../src/firefox_extension/lib/parser
|
||||
|
||||
function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
||||
expect(actualEvents.length == expectedEvents.length);
|
||||
for (var i = 0; i < actualEvents.length; ++i) {
|
||||
var actualEvent = actualEvents[i];
|
||||
var expectedEvent = expectedEvents[i];
|
||||
for (var key in actualEvent) {
|
||||
for (let i = 0; i < actualEvents.length; ++i) {
|
||||
const actualEvent = actualEvents[i];
|
||||
const expectedEvent = expectedEvents[i];
|
||||
for (const key in actualEvent) {
|
||||
expect(actualEvent[key]).toEqual(expectedEvent[key]);
|
||||
}
|
||||
}
|
||||
@ -22,17 +22,17 @@ function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
||||
export function main() {
|
||||
describe('convertPerfProfileToEvents', function() {
|
||||
it('should convert single instantaneous event', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [
|
||||
{samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]}
|
||||
]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||
});
|
||||
|
||||
it('should convert single non-instantaneous event', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -41,13 +41,13 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(
|
||||
perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]);
|
||||
});
|
||||
|
||||
it('should convert multiple instantaneous events', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -55,13 +55,13 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(
|
||||
perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]);
|
||||
});
|
||||
|
||||
it('should convert multiple mixed events', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -71,7 +71,7 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [
|
||||
{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'},
|
||||
{ph: 'B', ts: 5, name: 'script'}, {ph: 'E', ts: 10, name: 'script'}
|
||||
@ -79,13 +79,13 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should add args to gc events', function() {
|
||||
var profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]);
|
||||
});
|
||||
|
||||
it('should skip unknown events', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -93,7 +93,7 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||
});
|
||||
});
|
||||
|
@ -6,8 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var benchpress = require('../../index.js');
|
||||
var runner = new benchpress.Runner([
|
||||
import {$, browser} from 'protractor';
|
||||
|
||||
const benchpress = require('../../index.js');
|
||||
const runner = new benchpress.Runner([
|
||||
// use protractor as Webdriver client
|
||||
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||
// use RegressionSlopeValidator to validate samples
|
||||
|
@ -6,9 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
var found = false;
|
||||
for (var i = 0; i < events.length; ++i) {
|
||||
import {browser} from 'protractor';
|
||||
|
||||
const assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
let found = false;
|
||||
for (let i = 0; i < events.length; ++i) {
|
||||
if (events[i].name == eventName) {
|
||||
found = true;
|
||||
break;
|
||||
@ -18,7 +20,7 @@ var assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
};
|
||||
|
||||
describe('firefox extension', function() {
|
||||
var TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
||||
const TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
||||
|
||||
it('should measure performance', function() {
|
||||
browser.sleep(3000); // wait for extension to load
|
||||
|
@ -11,12 +11,12 @@ import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
||||
|
||||
export function main() {
|
||||
function createMetric(ids: any[]) {
|
||||
var m = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
.get(MultiMetric);
|
||||
const m = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
.get(MultiMetric);
|
||||
return Promise.resolve(m);
|
||||
}
|
||||
|
||||
@ -56,13 +56,13 @@ class MockMetric extends Metric {
|
||||
beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); }
|
||||
|
||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||
var result: {[key: string]: any} = {};
|
||||
const result: {[key: string]: any} = {};
|
||||
result[this._id] = {'restart': restart};
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
describe(): {[key: string]: string} {
|
||||
var result: {[key: string]: string} = {};
|
||||
const result: {[key: string]: string} = {};
|
||||
result[this._id] = 'describe';
|
||||
return result;
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ import {isPresent} from '../../src/facade/lang';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
var commandLog: any[];
|
||||
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
let commandLog: any[];
|
||||
const eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createMetric(
|
||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||
@ -34,7 +34,7 @@ export function main() {
|
||||
if (!microMetrics) {
|
||||
microMetrics = {};
|
||||
}
|
||||
var providers: Provider[] = [
|
||||
const providers: Provider[] = [
|
||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
||||
provide: PerflogMetric.SET_TIMEOUT,
|
||||
@ -66,7 +66,7 @@ export function main() {
|
||||
describe('perflog metric', () => {
|
||||
|
||||
function sortedKeys(stringMap: {[key: string]: any}) {
|
||||
var res: string[] = [];
|
||||
const res: string[] = [];
|
||||
res.push(...Object.keys(stringMap));
|
||||
res.sort();
|
||||
return res;
|
||||
@ -102,15 +102,15 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should describe itself based on micro metrics', () => {
|
||||
var description =
|
||||
const description =
|
||||
createMetric([[]], null, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe();
|
||||
expect(description['myMicroMetric']).toEqual('someDesc');
|
||||
});
|
||||
|
||||
it('should describe itself if frame capture is requested and available', () => {
|
||||
var description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
const description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
expect(description['frameTime.mean']).not.toContain('WARNING');
|
||||
expect(description['frameTime.best']).not.toContain('WARNING');
|
||||
expect(description['frameTime.worst']).not.toContain('WARNING');
|
||||
@ -118,9 +118,9 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should describe itself if frame capture is requested and not available', () => {
|
||||
var description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
const description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
expect(description['frameTime.mean']).toContain('WARNING');
|
||||
expect(description['frameTime.best']).toContain('WARNING');
|
||||
expect(description['frameTime.worst']).toContain('WARNING');
|
||||
@ -131,7 +131,7 @@ export function main() {
|
||||
|
||||
it('should not force gc and mark the timeline',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric([[]], null);
|
||||
const metric = createMetric([[]], null);
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
||||
|
||||
@ -141,7 +141,7 @@ export function main() {
|
||||
|
||||
it('should force gc and mark the timeline',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric([[]], null, {forceGc: true});
|
||||
const metric = createMetric([[]], null, {forceGc: true});
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
||||
|
||||
@ -155,11 +155,11 @@ export function main() {
|
||||
|
||||
it('should mark and aggregate events in between the marks',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [[
|
||||
const events = [[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10)
|
||||
]];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog'
|
||||
@ -172,13 +172,13 @@ export function main() {
|
||||
|
||||
it('should mark and aggregate events since navigationStart',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [[
|
||||
const events = [[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
||||
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(data['scriptTime']).toBe(1);
|
||||
|
||||
@ -187,7 +187,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
const events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.markEnd('benchpress0', 1),
|
||||
@ -195,7 +195,7 @@ export function main() {
|
||||
],
|
||||
[eventFactory.markEnd('benchpress1', 3)]
|
||||
];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((_) => metric.endMeasure(true))
|
||||
@ -211,7 +211,7 @@ export function main() {
|
||||
|
||||
it('should loop and aggregate until the end mark is present',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
const events = [
|
||||
[eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)],
|
||||
[eventFactory.end('script', 2)],
|
||||
[
|
||||
@ -219,7 +219,7 @@ export function main() {
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog',
|
||||
@ -233,7 +233,7 @@ export function main() {
|
||||
|
||||
it('should store events after the end mark for the next call',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
const events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1),
|
||||
eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1),
|
||||
@ -244,7 +244,7 @@ export function main() {
|
||||
eventFactory.markEnd('benchpress1', 6)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((data) => {
|
||||
@ -263,7 +263,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
describe('with forced gc', () => {
|
||||
var events: PerfLogEvent[][];
|
||||
let events: PerfLogEvent[][];
|
||||
beforeEach(() => {
|
||||
events = [[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||
@ -276,7 +276,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric(events, null, {forceGc: true});
|
||||
const metric = createMetric(events, null, {forceGc: true});
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
@ -291,7 +291,7 @@ export function main() {
|
||||
|
||||
it('should restart after the forced gc if needed',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric(events, null, {forceGc: true});
|
||||
const metric = createMetric(events, null, {forceGc: true});
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => {
|
||||
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
||||
|
||||
@ -313,7 +313,7 @@ export function main() {
|
||||
} = {}) {
|
||||
events.unshift(eventFactory.markStart('benchpress0', 0));
|
||||
events.push(eventFactory.markEnd('benchpress0', 10));
|
||||
var metric = createMetric([events], null, {
|
||||
const metric = createMetric([events], null, {
|
||||
microMetrics: microMetrics,
|
||||
captureFrames: captureFrames,
|
||||
receivedData: receivedData,
|
||||
@ -502,8 +502,8 @@ export function main() {
|
||||
|
||||
it('should ignore events from different processed as the start mark',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||
var metric = createMetric(
|
||||
const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||
const metric = createMetric(
|
||||
[[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null),
|
||||
eventFactory.end('script', 5, null),
|
||||
@ -685,7 +685,7 @@ class MockDriverExtension extends WebDriverExtension {
|
||||
readPerfLog(): Promise<any> {
|
||||
this._commandLog.push('readPerfLog');
|
||||
if (this._perfLogs.length > 0) {
|
||||
var next = this._perfLogs[0];
|
||||
const next = this._perfLogs[0];
|
||||
this._perfLogs.shift();
|
||||
return Promise.resolve(next);
|
||||
} else {
|
||||
|
@ -12,7 +12,7 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te
|
||||
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||
|
||||
export function main() {
|
||||
var wdAdapter: MockDriverAdapter;
|
||||
let wdAdapter: MockDriverAdapter;
|
||||
|
||||
function createMetric(
|
||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||
@ -25,7 +25,7 @@ export function main() {
|
||||
userMetrics = {};
|
||||
}
|
||||
wdAdapter = new MockDriverAdapter();
|
||||
var providers: Provider[] = [
|
||||
const providers: Provider[] = [
|
||||
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
||||
{provide: Options.USER_METRICS, useValue: userMetrics},
|
||||
{provide: WebDriverAdapter, useValue: wdAdapter}
|
||||
@ -45,7 +45,7 @@ export function main() {
|
||||
describe('endMeasure', () => {
|
||||
it('should stop measuring when all properties have numeric values',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
let metric = createMetric(
|
||||
const metric = createMetric(
|
||||
[[]], new PerfLogFeatures(),
|
||||
{userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}});
|
||||
metric.beginMeasure()
|
||||
@ -71,7 +71,7 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
executeScript(script: string): any {
|
||||
// Just handles `return window.propName` ignores `delete window.propName`.
|
||||
if (script.indexOf('return window.') == 0) {
|
||||
let metricName = script.substring('return window.'.length);
|
||||
const metricName = script.substring('return window.'.length);
|
||||
return Promise.resolve(this.data[metricName]);
|
||||
} else if (script.indexOf('delete window.') == 0) {
|
||||
return Promise.resolve(null);
|
||||
|
@ -14,8 +14,8 @@ import {isBlank, isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('console reporter', () => {
|
||||
var reporter: ConsoleReporter;
|
||||
var log: string[];
|
||||
let reporter: ConsoleReporter;
|
||||
let log: string[];
|
||||
|
||||
function createReporter(
|
||||
{columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
|
||||
@ -31,7 +31,7 @@ export function main() {
|
||||
if (sampleId == null) {
|
||||
sampleId = 'null';
|
||||
}
|
||||
var providers: Provider[] = [
|
||||
const providers: Provider[] = [
|
||||
ConsoleReporter.PROVIDERS, {
|
||||
provide: SampleDescription,
|
||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||
|
@ -13,7 +13,7 @@ import {isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('file reporter', () => {
|
||||
var loggedFile: any;
|
||||
let loggedFile: any;
|
||||
|
||||
function createReporter({sampleId, descriptions, metrics, path}: {
|
||||
sampleId: string,
|
||||
@ -21,7 +21,7 @@ export function main() {
|
||||
metrics: {[key: string]: string},
|
||||
path: string
|
||||
}) {
|
||||
var providers = [
|
||||
const providers = [
|
||||
JsonFileReporter.PROVIDERS, {
|
||||
provide: SampleDescription,
|
||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||
@ -49,9 +49,9 @@ export function main() {
|
||||
.reportSample(
|
||||
[mv(0, 0, {'a': 3, 'b': 6})],
|
||||
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
||||
var regExp = /somePath\/someId_\d+\.json/;
|
||||
const regExp = /somePath\/someId_\d+\.json/;
|
||||
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
||||
var parsedContent = JSON.parse(loggedFile['content']);
|
||||
const parsedContent = JSON.parse(loggedFile['content']);
|
||||
expect(parsedContent).toEqual({
|
||||
'description': {
|
||||
'id': 'someId',
|
||||
|
@ -12,12 +12,12 @@ import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../
|
||||
|
||||
export function main() {
|
||||
function createReporters(ids: any[]) {
|
||||
var r = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
.get(MultiReporter);
|
||||
const r = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
.get(MultiReporter);
|
||||
return Promise.resolve(r);
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ export function main() {
|
||||
|
||||
it('should reportMeasureValues to all',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var mv = new MeasureValues(0, new Date(), {});
|
||||
const mv = new MeasureValues(0, new Date(), {});
|
||||
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
||||
|
||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
||||
@ -34,9 +34,9 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var completeSample =
|
||||
const completeSample =
|
||||
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
||||
var validSample = [completeSample[1]];
|
||||
const validSample = [completeSample[1]];
|
||||
|
||||
createReporters(['m1', 'm2'])
|
||||
.then((r) => r.reportSample(completeSample, validSample))
|
||||
|
@ -12,8 +12,8 @@ import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription
|
||||
|
||||
export function main() {
|
||||
describe('runner', () => {
|
||||
var injector: ReflectiveInjector;
|
||||
var runner: Runner;
|
||||
let injector: ReflectiveInjector;
|
||||
let runner: Runner;
|
||||
|
||||
function createRunner(defaultProviders: any[] = null): Runner {
|
||||
if (!defaultProviders) {
|
||||
@ -76,7 +76,7 @@ export function main() {
|
||||
|
||||
it('should provide Options.EXECUTE',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var execute = () => {};
|
||||
const execute = () => {};
|
||||
createRunner().sample({id: 'someId', execute: execute}).then((_) => {
|
||||
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
||||
async.done();
|
||||
@ -85,7 +85,7 @@ export function main() {
|
||||
|
||||
it('should provide Options.PREPARE',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var prepare = () => {};
|
||||
const prepare = () => {};
|
||||
createRunner().sample({id: 'someId', prepare: prepare}).then((_) => {
|
||||
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
||||
async.done();
|
||||
|
@ -12,10 +12,10 @@ import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, V
|
||||
import {isBlank, isPresent} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
var EMPTY_EXECUTE = () => {};
|
||||
const EMPTY_EXECUTE = () => {};
|
||||
|
||||
describe('sampler', () => {
|
||||
var sampler: Sampler;
|
||||
let sampler: Sampler;
|
||||
|
||||
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
||||
driver?: any,
|
||||
@ -25,7 +25,7 @@ export function main() {
|
||||
prepare?: any,
|
||||
execute?: any
|
||||
} = {}) {
|
||||
var time = 1000;
|
||||
let time = 1000;
|
||||
if (!metric) {
|
||||
metric = new MockMetric([]);
|
||||
}
|
||||
@ -35,7 +35,7 @@ export function main() {
|
||||
if (isBlank(driver)) {
|
||||
driver = new MockDriverAdapter([]);
|
||||
}
|
||||
var providers = [
|
||||
const providers = [
|
||||
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
||||
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
||||
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
||||
@ -50,10 +50,10 @@ export function main() {
|
||||
|
||||
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var count = 0;
|
||||
var driver = new MockDriverAdapter([], (callback: Function) => {
|
||||
var result = callback();
|
||||
const log: any[] = [];
|
||||
let count = 0;
|
||||
const driver = new MockDriverAdapter([], (callback: Function) => {
|
||||
const result = callback();
|
||||
log.push(result);
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
@ -73,8 +73,8 @@ export function main() {
|
||||
|
||||
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var workCount = 0;
|
||||
var log: any[] = [];
|
||||
let workCount = 0;
|
||||
const log: any[] = [];
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
@ -98,8 +98,8 @@ export function main() {
|
||||
|
||||
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var workCount = 0;
|
||||
const log: any[] = [];
|
||||
let workCount = 0;
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
@ -120,14 +120,14 @@ export function main() {
|
||||
|
||||
it('should only collect metrics for execute and ignore metrics from prepare',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var scriptTime = 0;
|
||||
var iterationCount = 1;
|
||||
let scriptTime = 0;
|
||||
let iterationCount = 1;
|
||||
createSampler({
|
||||
validator: createCountingValidator(2),
|
||||
metric: new MockMetric(
|
||||
[],
|
||||
() => {
|
||||
var result = Promise.resolve({'script': scriptTime});
|
||||
const result = Promise.resolve({'script': scriptTime});
|
||||
scriptTime = 0;
|
||||
return result;
|
||||
}),
|
||||
@ -147,8 +147,8 @@ export function main() {
|
||||
|
||||
it('should call the validator for every execution and store the valid sample',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var validSample = [mv(null, null, {})];
|
||||
const log: any[] = [];
|
||||
const validSample = [mv(null, null, {})];
|
||||
|
||||
createSampler({
|
||||
metric: createCountingMetric(),
|
||||
@ -174,8 +174,8 @@ export function main() {
|
||||
|
||||
it('should report the metric values',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var validSample = [mv(null, null, {})];
|
||||
const log: any[] = [];
|
||||
const validSample = [mv(null, null, {})];
|
||||
createSampler({
|
||||
validator: createCountingValidator(2, validSample),
|
||||
metric: createCountingMetric(),
|
||||
@ -220,7 +220,7 @@ function createCountingValidator(
|
||||
}
|
||||
|
||||
function createCountingMetric(log: any[] = []) {
|
||||
var scriptTime = 0;
|
||||
let scriptTime = 0;
|
||||
return new MockMetric(log, () => ({'script': scriptTime++}));
|
||||
}
|
||||
|
||||
@ -239,7 +239,8 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
class MockValidator extends Validator {
|
||||
constructor(private _log: any[] = [], private _validate: Function = null) { super(); }
|
||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||
var stableSample = isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||
const stableSample =
|
||||
isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||
this._log.push(['validate', completeSample, stableSample]);
|
||||
return stableSample;
|
||||
}
|
||||
@ -252,7 +253,7 @@ class MockMetric extends Metric {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
endMeasure(restart: boolean) {
|
||||
var measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||
const measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||
this._log.push(['endMeasure', restart, measureValues]);
|
||||
return Promise.resolve(measureValues);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export class TraceEventFactory {
|
||||
constructor(private _cat: string, private _pid: string) {}
|
||||
|
||||
create(ph: any, name: string, time: number, args: any = null) {
|
||||
var res:
|
||||
const res:
|
||||
PerfLogEvent = {'name': name, 'cat': this._cat, 'ph': ph, 'ts': time, 'pid': this._pid};
|
||||
if (isPresent(args)) {
|
||||
res['args'] = args;
|
||||
@ -34,7 +34,7 @@ export class TraceEventFactory {
|
||||
}
|
||||
|
||||
complete(name: string, time: number, duration: number, args: any = null) {
|
||||
var res = this.create('X', name, time, args);
|
||||
const res = this.create('X', name, time, args);
|
||||
res['dur'] = duration;
|
||||
return res;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../..
|
||||
|
||||
export function main() {
|
||||
describe('regression slope validator', () => {
|
||||
var validator: RegressionSlopeValidator;
|
||||
let validator: RegressionSlopeValidator;
|
||||
|
||||
function createValidator({size, metric}: {size: number, metric: string}) {
|
||||
validator = ReflectiveInjector
|
||||
@ -42,14 +42,14 @@ export function main() {
|
||||
|
||||
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||
const sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||
});
|
||||
|
||||
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||
const sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||
|
||||
export function main() {
|
||||
describe('size validator', () => {
|
||||
var validator: SizeValidator;
|
||||
let validator: SizeValidator;
|
||||
|
||||
function createValidator(size: number) {
|
||||
validator =
|
||||
@ -35,7 +35,7 @@ export function main() {
|
||||
|
||||
it('should return the last sampleSize runs when it has at least the given size', () => {
|
||||
createValidator(2);
|
||||
var sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||
const sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||
});
|
||||
|
@ -14,23 +14,23 @@ import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('chrome driver extension', () => {
|
||||
var CHROME45_USER_AGENT =
|
||||
const CHROME45_USER_AGENT =
|
||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
||||
|
||||
var log: any[];
|
||||
var extension: ChromeDriverExtension;
|
||||
let log: any[];
|
||||
let extension: ChromeDriverExtension;
|
||||
|
||||
var blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||
var v8Events = new TraceEventFactory('v8', 'pid0');
|
||||
var v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||
var chromeTimelineEvents =
|
||||
const blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||
const v8Events = new TraceEventFactory('v8', 'pid0');
|
||||
const v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||
const chromeTimelineEvents =
|
||||
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||
var chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
||||
var chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
||||
var chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
||||
var chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
||||
var benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
const chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
||||
const chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
||||
const chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
||||
const chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
||||
const benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
||||
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(
|
||||
perfRecords: any[] = null, userAgent: string = null,
|
||||
@ -101,7 +101,7 @@ export function main() {
|
||||
|
||||
it('should normalize "tdur" to "dur"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||
const event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event]).readPerfLog().then((events) => {
|
||||
expect(events).toEqual([
|
||||
|
@ -13,10 +13,10 @@ import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('ios driver extension', () => {
|
||||
var log: any[];
|
||||
var extension: IOsDriverExtension;
|
||||
let log: any[];
|
||||
let extension: IOsDriverExtension;
|
||||
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
||||
if (!perfRecords) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {isListLikeIterable} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {isPresent, stringify} from '../facade/lang';
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
@ -108,8 +108,14 @@ export class NgClass implements DoCheck {
|
||||
}
|
||||
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem(
|
||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, true));
|
||||
changes.forEachAddedItem((record: CollectionChangeRecord) => {
|
||||
if (typeof record.item === 'string') {
|
||||
this._toggleClass(record.item, true);
|
||||
} else {
|
||||
throw new Error(
|
||||
`NgClass can only toggle CSS classes expressed as strings, got ${stringify(record.item)}`);
|
||||
}
|
||||
});
|
||||
|
||||
changes.forEachRemovedItem(
|
||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
||||
|
@ -150,13 +150,13 @@ export class NgFor implements DoCheck, OnChanges {
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
||||
* @description
|
||||
*
|
||||
* The styles are updated according to the value of the expression evaluation:
|
||||
* - keys are style names with an option `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||
* - keys are style names with an optional `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||
* - values are the values assigned to those properties (expressed in the given unit).
|
||||
*
|
||||
* @stable
|
||||
|
@ -6,19 +6,31 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
|
||||
const _CASE_DEFAULT = {};
|
||||
import {Directive, DoCheck, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
export class SwitchView {
|
||||
private _created = false;
|
||||
|
||||
constructor(
|
||||
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
||||
|
||||
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||
create(): void {
|
||||
this._created = true;
|
||||
this._viewContainerRef.createEmbeddedView(this._templateRef);
|
||||
}
|
||||
|
||||
destroy(): void { this._viewContainerRef.clear(); }
|
||||
destroy(): void {
|
||||
this._created = false;
|
||||
this._viewContainerRef.clear();
|
||||
}
|
||||
|
||||
enforceState(created: boolean) {
|
||||
if (created && !this._created) {
|
||||
this.create();
|
||||
} else if (!created && this._created) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,92 +76,52 @@ export class SwitchView {
|
||||
*/
|
||||
@Directive({selector: '[ngSwitch]'})
|
||||
export class NgSwitch {
|
||||
private _switchValue: any;
|
||||
private _useDefault: boolean = false;
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
private _defaultViews: SwitchView[];
|
||||
private _defaultUsed = false;
|
||||
private _caseCount = 0;
|
||||
private _lastCaseCheckIndex = 0;
|
||||
private _lastCasesMatched = false;
|
||||
private _ngSwitch: any;
|
||||
|
||||
@Input()
|
||||
set ngSwitch(value: any) {
|
||||
// Set of views to display for this value
|
||||
let views = this._valueViews.get(value);
|
||||
|
||||
if (views) {
|
||||
this._useDefault = false;
|
||||
} else {
|
||||
// No view to display for the current value -> default case
|
||||
// Nothing to do if the default case was already active
|
||||
if (this._useDefault) {
|
||||
return;
|
||||
}
|
||||
this._useDefault = true;
|
||||
views = this._valueViews.get(_CASE_DEFAULT);
|
||||
}
|
||||
|
||||
this._emptyAllActiveViews();
|
||||
this._activateViews(views);
|
||||
this._switchValue = value;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_onCaseValueChanged(oldCase: any, newCase: any, view: SwitchView): void {
|
||||
this._deregisterView(oldCase, view);
|
||||
this._registerView(newCase, view);
|
||||
|
||||
if (oldCase === this._switchValue) {
|
||||
view.destroy();
|
||||
ListWrapper.remove(this._activeViews, view);
|
||||
} else if (newCase === this._switchValue) {
|
||||
if (this._useDefault) {
|
||||
this._useDefault = false;
|
||||
this._emptyAllActiveViews();
|
||||
}
|
||||
view.create();
|
||||
this._activeViews.push(view);
|
||||
}
|
||||
|
||||
// Switch to default when there is no more active ViewContainers
|
||||
if (this._activeViews.length === 0 && !this._useDefault) {
|
||||
this._useDefault = true;
|
||||
this._activateViews(this._valueViews.get(_CASE_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
private _emptyAllActiveViews(): void {
|
||||
const activeContainers = this._activeViews;
|
||||
for (var i = 0; i < activeContainers.length; i++) {
|
||||
activeContainers[i].destroy();
|
||||
}
|
||||
this._activeViews = [];
|
||||
}
|
||||
|
||||
private _activateViews(views?: SwitchView[]): void {
|
||||
if (views) {
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
views[i].create();
|
||||
}
|
||||
this._activeViews = views;
|
||||
set ngSwitch(newValue: any) {
|
||||
this._ngSwitch = newValue;
|
||||
if (this._caseCount === 0) {
|
||||
this._updateDefaultCases(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_registerView(value: any, view: SwitchView): void {
|
||||
let views = this._valueViews.get(value);
|
||||
if (!views) {
|
||||
views = [];
|
||||
this._valueViews.set(value, views);
|
||||
_addCase(): number { return this._caseCount++; }
|
||||
|
||||
/** @internal */
|
||||
_addDefault(view: SwitchView) {
|
||||
if (!this._defaultViews) {
|
||||
this._defaultViews = [];
|
||||
}
|
||||
views.push(view);
|
||||
this._defaultViews.push(view);
|
||||
}
|
||||
|
||||
private _deregisterView(value: any, view: SwitchView): void {
|
||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||
if (value === _CASE_DEFAULT) return;
|
||||
const views = this._valueViews.get(value);
|
||||
if (views.length == 1) {
|
||||
this._valueViews.delete(value);
|
||||
} else {
|
||||
ListWrapper.remove(views, view);
|
||||
/** @internal */
|
||||
_matchCase(value: any): boolean {
|
||||
const matched = value == this._ngSwitch;
|
||||
this._lastCasesMatched = this._lastCasesMatched || matched;
|
||||
this._lastCaseCheckIndex++;
|
||||
if (this._lastCaseCheckIndex === this._caseCount) {
|
||||
this._updateDefaultCases(!this._lastCasesMatched);
|
||||
this._lastCaseCheckIndex = 0;
|
||||
this._lastCasesMatched = false;
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
private _updateDefaultCases(useDefault: boolean) {
|
||||
if (this._defaultViews && useDefault !== this._defaultUsed) {
|
||||
this._defaultUsed = useDefault;
|
||||
for (let i = 0; i < this._defaultViews.length; i++) {
|
||||
const defaultView = this._defaultViews[i];
|
||||
defaultView.enforceState(useDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,24 +151,20 @@ export class NgSwitch {
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchCase]'})
|
||||
export class NgSwitchCase {
|
||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||
private _value: any = _CASE_DEFAULT;
|
||||
export class NgSwitchCase implements DoCheck {
|
||||
private _view: SwitchView;
|
||||
private _switch: NgSwitch;
|
||||
|
||||
@Input()
|
||||
ngSwitchCase: any;
|
||||
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
this._switch = ngSwitch;
|
||||
@Host() private ngSwitch: NgSwitch) {
|
||||
ngSwitch._addCase();
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchCase(value: any) {
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,7 +194,7 @@ export class NgSwitchCase {
|
||||
export class NgSwitchDefault {
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() sswitch: NgSwitch) {
|
||||
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
ngSwitch._addDefault(new SwitchView(viewContainer, templateRef));
|
||||
}
|
||||
}
|
||||
|
@ -64,19 +64,19 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
path(includeHash: boolean = false): string {
|
||||
// the hash value is always prefixed with a `#`
|
||||
// and if it is empty then it will stay empty
|
||||
var path = this._platformLocation.hash;
|
||||
let path = this._platformLocation.hash;
|
||||
if (!isPresent(path)) path = '#';
|
||||
|
||||
return path.length > 0 ? path.substring(1) : path;
|
||||
}
|
||||
|
||||
prepareExternalUrl(internal: string): string {
|
||||
var url = Location.joinWithSlash(this._baseHref, internal);
|
||||
const url = Location.joinWithSlash(this._baseHref, internal);
|
||||
return url.length > 0 ? ('#' + url) : url;
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
let url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
@ -84,7 +84,7 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
let url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ export class Location {
|
||||
if (end.length == 0) {
|
||||
return start;
|
||||
}
|
||||
var slashes = 0;
|
||||
let slashes = 0;
|
||||
if (start.endsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
|
@ -79,12 +79,12 @@ export class PathLocationStrategy extends LocationStrategy {
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
const externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
this._platformLocation.pushState(state, title, externalUrl);
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, url: string, queryParams: string) {
|
||||
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
const externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
this._platformLocation.replaceState(state, title, externalUrl);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
import {DateFormatter} from '../facade/intl';
|
||||
import {NumberWrapper, isBlank, isDate} from '../facade/lang';
|
||||
import {NumberWrapper, isDate} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
|
||||
@ -54,6 +54,9 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
*
|
||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||
*
|
||||
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
|
||||
* applied and the formatted text will have the same day, month and year of the expression.
|
||||
*
|
||||
* WARNINGS:
|
||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||
* Instead users should treat the date as an immutable object and change the reference when the
|
||||
@ -95,22 +98,42 @@ export class DatePipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, pattern: string = 'mediumDate'): string {
|
||||
let date: Date;
|
||||
|
||||
if (isBlank(value)) return null;
|
||||
|
||||
if (!this.supports(value)) {
|
||||
if (typeof value === 'string') {
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
if (isDate(value)) {
|
||||
date = value;
|
||||
} else if (NumberWrapper.isNumeric(value)) {
|
||||
date = new Date(parseFloat(value));
|
||||
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
||||
/**
|
||||
* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
||||
* before Date creation to avoid time offset and errors in the new Date.
|
||||
* If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
||||
* date, some browsers (e.g. IE 9) will throw an invalid Date error
|
||||
* If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
|
||||
* is applied
|
||||
* Note: ISO months are 0 for January, 1 for February, ...
|
||||
*/
|
||||
const [y, m, d] = value.split('-').map((val: string) => parseInt(val, 10));
|
||||
date = new Date(y, m - 1, d);
|
||||
} else {
|
||||
date = new Date(value);
|
||||
}
|
||||
|
||||
if (!isDate(date)) {
|
||||
throw new InvalidPipeArgumentError(DatePipe, value);
|
||||
}
|
||||
|
||||
if (NumberWrapper.isNumeric(value)) {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
|
||||
return DateFormatter.format(
|
||||
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||
}
|
||||
|
||||
private supports(obj: any): boolean {
|
||||
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
|
||||
(typeof obj === 'string' && isDate(new Date(obj)));
|
||||
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||
}
|
||||
}
|
||||
|
||||
function isBlank(obj: any): boolean {
|
||||
return obj == null || obj === '';
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
@ -16,9 +15,10 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* @howToUse `expression | i18nSelect:mapping`
|
||||
* @description
|
||||
*
|
||||
* Where:
|
||||
* - `mapping`: is an object that indicates the text that should be displayed
|
||||
* Where `mapping` is an object that indicates the text that should be displayed
|
||||
* for different values of the provided `expression`.
|
||||
* If none of the keys of the mapping match the value of the `expression`, then the content
|
||||
* of the `other` key is returned when present, otherwise an empty string is returned.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
@ -29,12 +29,20 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
@Pipe({name: 'i18nSelect', pure: true})
|
||||
export class I18nSelectPipe implements PipeTransform {
|
||||
transform(value: string, mapping: {[key: string]: string}): string {
|
||||
if (isBlank(value)) return '';
|
||||
if (value == null) return '';
|
||||
|
||||
if (typeof mapping !== 'object' || mapping === null) {
|
||||
if (typeof mapping !== 'object' || typeof value !== 'string') {
|
||||
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
|
||||
}
|
||||
|
||||
return mapping[value] || '';
|
||||
if (mapping.hasOwnProperty(value)) {
|
||||
return mapping[value];
|
||||
}
|
||||
|
||||
if (mapping.hasOwnProperty('other')) {
|
||||
return mapping['other'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* @howToUse `expression | lowercase`
|
||||
* @description
|
||||
*
|
||||
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
||||
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -37,7 +37,7 @@ function formatNumber(
|
||||
}
|
||||
|
||||
if (digits) {
|
||||
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
const parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
if (parts === null) {
|
||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* @howToUse `expression | uppercase`
|
||||
* @description
|
||||
*
|
||||
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
||||
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS class list', () => {
|
||||
@ -66,7 +65,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression object', async(() => {
|
||||
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
detectChangesAndExpectClassName('foo');
|
||||
|
||||
@ -135,7 +134,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression', async(() => {
|
||||
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||
let arrExpr = getComponent().arrExpr;
|
||||
const arrExpr = getComponent().arrExpr;
|
||||
detectChangesAndExpectClassName('foo');
|
||||
|
||||
arrExpr.push('bar');
|
||||
@ -187,6 +186,13 @@ export function main() {
|
||||
getComponent().arrExpr = ['foo bar baz foobar'];
|
||||
detectChangesAndExpectClassName('foo bar baz foobar');
|
||||
}));
|
||||
|
||||
it('should throw with descriptive error message when CSS class is not a string', () => {
|
||||
fixture = createTestComponent(`<div [ngClass]="['foo', {}]"></div>`);
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(
|
||||
/NgClass can only toggle CSS classes expressed as strings, got \[object Object\]/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('expressions evaluating to sets', () => {
|
||||
@ -253,7 +259,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the class attribute', async(() => {
|
||||
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
objExpr['bar'] = true;
|
||||
detectChangesAndExpectClassName('init foo bar');
|
||||
@ -267,7 +273,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the interpolated class attribute', async(() => {
|
||||
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
objExpr['bar'] = true;
|
||||
detectChangesAndExpectClassName(`init foo bar`);
|
||||
@ -282,7 +288,7 @@ export function main() {
|
||||
it('should co-operate with the class attribute and binding to it', async(() => {
|
||||
fixture =
|
||||
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
objExpr['bar'] = true;
|
||||
detectChangesAndExpectClassName(`init foo bar`);
|
||||
@ -298,7 +304,7 @@ export function main() {
|
||||
const template =
|
||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||
fixture = createTestComponent(template);
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
detectChangesAndExpectClassName('init foo baz');
|
||||
|
||||
@ -316,7 +322,7 @@ export function main() {
|
||||
async(() => {
|
||||
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||
fixture = createTestComponent(template);
|
||||
let cmp = getComponent();
|
||||
const cmp = getComponent();
|
||||
|
||||
detectChangesAndExpectClassName('init foo');
|
||||
|
||||
@ -348,4 +354,4 @@ class TestComponent {
|
||||
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.createComponent(TestComponent);
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should use a default template if a custom one is null', async(() => {
|
||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
||||
const cutTemplate =
|
||||
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {Attribute, Component, Directive} from '@angular/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
@ -32,93 +32,153 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('switch value changes', () => {
|
||||
it('should switch amongst when values', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
it('should switch amongst when values', () => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
detectChangesAndExpectText('');
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b');
|
||||
}));
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b');
|
||||
});
|
||||
|
||||
it('should switch amongst when values with fallback to default', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||
'<li template="ngSwitchDefault">when default</li>' +
|
||||
'</ul></div>';
|
||||
it('should switch amongst when values with fallback to default', () => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||
'<li template="ngSwitchDefault">when default</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default');
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when default');
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when default');
|
||||
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default');
|
||||
}));
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default');
|
||||
});
|
||||
|
||||
it('should support multiple whens with the same value', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'</ul></div>';
|
||||
it('should support multiple whens with the same value', () => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a1;when a2;');
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a1;when a2;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
}));
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when values changes', () => {
|
||||
it('should switch amongst when values', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||
'</ul></div>';
|
||||
it('should switch amongst when values', () => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().when1 = 'a';
|
||||
getComponent().when2 = 'b';
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().when1 = 'a';
|
||||
getComponent().when2 = 'b';
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when 2;');
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when 2;');
|
||||
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default;');
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default;');
|
||||
|
||||
getComponent().when1 = 'c';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
getComponent().when1 = 'c';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
|
||||
getComponent().when1 = 'd';
|
||||
detectChangesAndExpectText('when default;');
|
||||
}));
|
||||
getComponent().when1 = 'd';
|
||||
detectChangesAndExpectText('when default;');
|
||||
});
|
||||
});
|
||||
|
||||
describe('corner cases', () => {
|
||||
|
||||
it('should not create the default case if another case matches', () => {
|
||||
const log: string[] = [];
|
||||
|
||||
@Directive({selector: '[test]'})
|
||||
class TestDirective {
|
||||
constructor(@Attribute('test') test: string) { log.push(test); }
|
||||
}
|
||||
|
||||
const template = '<div [ngSwitch]="switchValue">' +
|
||||
'<div *ngSwitchCase="\'a\'" test="aCase"></div>' +
|
||||
'<div *ngSwitchDefault test="defaultCase"></div>' +
|
||||
'</div>';
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestDirective]});
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.createComponent(TestComponent);
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.switchValue = 'a';
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(log).toEqual(['aCase']);
|
||||
});
|
||||
|
||||
it('should create the default case if there is no other case', () => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
});
|
||||
|
||||
it('should allow defaults before cases', () => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a1;when a2;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ export function main() {
|
||||
describe('AsyncPipe', () => {
|
||||
|
||||
describe('Observable', () => {
|
||||
var emitter: EventEmitter<any>;
|
||||
var pipe: AsyncPipe;
|
||||
var ref: any;
|
||||
var message = {};
|
||||
let emitter: EventEmitter<any>;
|
||||
let pipe: AsyncPipe;
|
||||
let ref: any;
|
||||
const message = {};
|
||||
|
||||
beforeEach(() => {
|
||||
emitter = new EventEmitter();
|
||||
@ -62,7 +62,7 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(emitter);
|
||||
|
||||
var newEmitter = new EventEmitter();
|
||||
const newEmitter = new EventEmitter();
|
||||
expect(pipe.transform(newEmitter)).toBe(null);
|
||||
emitter.emit(message);
|
||||
|
||||
@ -104,14 +104,14 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('Promise', () => {
|
||||
var message = new Object();
|
||||
var pipe: AsyncPipe;
|
||||
var resolve: (result: any) => void;
|
||||
var reject: (error: any) => void;
|
||||
var promise: Promise<any>;
|
||||
var ref: SpyChangeDetectorRef;
|
||||
const message = new Object();
|
||||
let pipe: AsyncPipe;
|
||||
let resolve: (result: any) => void;
|
||||
let reject: (error: any) => void;
|
||||
let promise: Promise<any>;
|
||||
let ref: SpyChangeDetectorRef;
|
||||
// adds longer timers for passing tests in IE
|
||||
var timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||
const timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||
|
||||
beforeEach(() => {
|
||||
promise = new Promise((res, rej) => {
|
||||
@ -154,7 +154,7 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(promise);
|
||||
|
||||
var promise = new Promise<any>(() => {});
|
||||
promise = new Promise<any>(() => {});
|
||||
expect(pipe.transform(promise)).toBe(null);
|
||||
|
||||
// this should not affect the pipe, so it should return WrappedValue
|
||||
@ -168,7 +168,7 @@ export function main() {
|
||||
|
||||
it('should request a change detection check upon receiving a new value',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var markForCheck = ref.spy('markForCheck');
|
||||
const markForCheck = ref.spy('markForCheck');
|
||||
pipe.transform(promise);
|
||||
resolve(message);
|
||||
|
||||
@ -202,14 +202,14 @@ export function main() {
|
||||
|
||||
describe('null', () => {
|
||||
it('should return null when given null', () => {
|
||||
var pipe = new AsyncPipe(null);
|
||||
const pipe = new AsyncPipe(null);
|
||||
expect(pipe.transform(null)).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('other types', () => {
|
||||
it('should throw when given an invalid object', () => {
|
||||
var pipe = new AsyncPipe(null);
|
||||
const pipe = new AsyncPipe(null);
|
||||
expect(() => pipe.transform(<any>'some bogus object')).toThrowError();
|
||||
});
|
||||
});
|
||||
|
@ -8,13 +8,18 @@
|
||||
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
export function main() {
|
||||
describe('DatePipe', () => {
|
||||
var date: Date;
|
||||
var pipe: DatePipe;
|
||||
let date: Date;
|
||||
const isoStringWithoutTime = '2015-01-01';
|
||||
let pipe: DatePipe;
|
||||
|
||||
// Check the transformation of a date into a pattern
|
||||
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
|
||||
expect(pipe.transform(date, pattern)).toEqual(output);
|
||||
}
|
||||
|
||||
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
|
||||
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
||||
@ -34,7 +39,9 @@ export function main() {
|
||||
|
||||
describe('supports', () => {
|
||||
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
||||
|
||||
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
||||
|
||||
it('should support numeric strings',
|
||||
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
||||
|
||||
@ -42,88 +49,143 @@ export function main() {
|
||||
() => { expect(() => pipe.transform('123456789.11')).not.toThrow(); });
|
||||
|
||||
it('should support ISO string',
|
||||
() => { expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow(); });
|
||||
() => expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow());
|
||||
|
||||
it('should not support other objects', () => {
|
||||
expect(() => pipe.transform({})).toThrow();
|
||||
expect(() => pipe.transform('')).toThrow();
|
||||
});
|
||||
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
||||
|
||||
it('should support ISO string without time',
|
||||
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform({})).toThrowError(); });
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
it('should format each component correctly', () => {
|
||||
expect(pipe.transform(date, 'y')).toEqual('2015');
|
||||
expect(pipe.transform(date, 'yy')).toEqual('15');
|
||||
expect(pipe.transform(date, 'M')).toEqual('6');
|
||||
expect(pipe.transform(date, 'MM')).toEqual('06');
|
||||
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
|
||||
expect(pipe.transform(date, 'MMMM')).toEqual('June');
|
||||
expect(pipe.transform(date, 'd')).toEqual('15');
|
||||
expect(pipe.transform(date, 'EEE')).toEqual('Mon');
|
||||
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
|
||||
const dateFixtures: any = {
|
||||
'y': '2015',
|
||||
'yy': '15',
|
||||
'M': '6',
|
||||
'MM': '06',
|
||||
'MMM': 'Jun',
|
||||
'MMMM': 'June',
|
||||
'd': '15',
|
||||
'dd': '15',
|
||||
'EEE': 'Mon',
|
||||
'EEEE': 'Monday'
|
||||
};
|
||||
|
||||
const isoStringWithoutTimeFixtures: any = {
|
||||
'y': '2015',
|
||||
'yy': '15',
|
||||
'M': '1',
|
||||
'MM': '01',
|
||||
'MMM': 'Jan',
|
||||
'MMMM': 'January',
|
||||
'd': '1',
|
||||
'dd': '01',
|
||||
'EEE': 'Thu',
|
||||
'EEEE': 'Thursday'
|
||||
};
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'h')).toEqual('9');
|
||||
expect(pipe.transform(date, 'hh')).toEqual('09');
|
||||
expect(pipe.transform(date, 'j')).toEqual('9 AM');
|
||||
dateFixtures['h'] = '9';
|
||||
dateFixtures['hh'] = '09';
|
||||
dateFixtures['j'] = '9 AM';
|
||||
isoStringWithoutTimeFixtures['h'] = '12';
|
||||
isoStringWithoutTimeFixtures['hh'] = '12';
|
||||
isoStringWithoutTimeFixtures['j'] = '12 AM';
|
||||
}
|
||||
|
||||
// IE and Edge can't format a date to minutes and seconds without hours
|
||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||
!browserDetection.supportsNativeIntlApi) {
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'HH')).toEqual('09');
|
||||
dateFixtures['HH'] = '09';
|
||||
isoStringWithoutTimeFixtures['HH'] = '00';
|
||||
}
|
||||
|
||||
expect(pipe.transform(date, 'E')).toEqual('M');
|
||||
expect(pipe.transform(date, 'L')).toEqual('J');
|
||||
expect(pipe.transform(date, 'm')).toEqual('3');
|
||||
expect(pipe.transform(date, 's')).toEqual('1');
|
||||
expect(pipe.transform(date, 'mm')).toEqual('03');
|
||||
expect(pipe.transform(date, 'ss')).toEqual('01');
|
||||
dateFixtures['E'] = 'M';
|
||||
dateFixtures['L'] = 'J';
|
||||
dateFixtures['m'] = '3';
|
||||
dateFixtures['s'] = '1';
|
||||
dateFixtures['mm'] = '03';
|
||||
dateFixtures['ss'] = '01';
|
||||
isoStringWithoutTimeFixtures['m'] = '0';
|
||||
isoStringWithoutTimeFixtures['s'] = '0';
|
||||
isoStringWithoutTimeFixtures['mm'] = '00';
|
||||
isoStringWithoutTimeFixtures['ss'] = '00';
|
||||
}
|
||||
|
||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||
});
|
||||
|
||||
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
||||
});
|
||||
|
||||
expect(pipe.transform(date, 'Z')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format common multi component patterns', () => {
|
||||
expect(pipe.transform(date, 'EEE, M/d/y')).toEqual('Mon, 6/15/2015');
|
||||
expect(pipe.transform(date, 'EEE, M/d')).toEqual('Mon, 6/15');
|
||||
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
|
||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'yMEEEd')).toEqual('20156Mon15');
|
||||
expect(pipe.transform(date, 'MEEEd')).toEqual('6Mon15');
|
||||
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
|
||||
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
||||
const dateFixtures: any = {
|
||||
'EEE, M/d/y': 'Mon, 6/15/2015',
|
||||
'EEE, M/d': 'Mon, 6/15',
|
||||
'MMM d': 'Jun 15',
|
||||
'dd/MM/yyyy': '15/06/2015',
|
||||
'MM/dd/yyyy': '06/15/2015',
|
||||
'yMEEEd': '20156Mon15',
|
||||
'MEEEd': '6Mon15',
|
||||
'MMMd': 'Jun15',
|
||||
'yMMMMEEEEd': 'Monday, June 15, 2015'
|
||||
};
|
||||
|
||||
// IE and Edge can't format a date to minutes and seconds without hours
|
||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||
!browserDetection.supportsNativeIntlApi) {
|
||||
expect(pipe.transform(date, 'ms')).toEqual('31');
|
||||
dateFixtures['ms'] = '31';
|
||||
}
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'jm')).toEqual('9:03 AM');
|
||||
dateFixtures['jm'] = '9:03 AM';
|
||||
}
|
||||
|
||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should format with pattern aliases', () => {
|
||||
const dateFixtures: any = {
|
||||
'MM/dd/yyyy': '06/15/2015',
|
||||
'fullDate': 'Monday, June 15, 2015',
|
||||
'longDate': 'June 15, 2015',
|
||||
'mediumDate': 'Jun 15, 2015',
|
||||
'shortDate': '6/15/2015'
|
||||
};
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
// IE and Edge do not add a coma after the year in these 2 cases
|
||||
if ((browserDetection.isEdge || browserDetection.isIE) &&
|
||||
browserDetection.supportsNativeIntlApi) {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015 9:03:01 AM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015 9:03 AM');
|
||||
dateFixtures['medium'] = 'Jun 15, 2015 9:03:01 AM';
|
||||
dateFixtures['short'] = '6/15/2015 9:03 AM';
|
||||
} else {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 AM');
|
||||
dateFixtures['medium'] = 'Jun 15, 2015, 9:03:01 AM';
|
||||
dateFixtures['short'] = '6/15/2015, 9:03 AM';
|
||||
}
|
||||
}
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
|
||||
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
|
||||
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
|
||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
|
||||
dateFixtures['mediumTime'] = '9:03:01 AM';
|
||||
dateFixtures['shortTime'] = '9:03 AM';
|
||||
}
|
||||
|
||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should remove bidi control characters',
|
||||
|
@ -12,10 +12,10 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
||||
|
||||
export function main() {
|
||||
describe('I18nPluralPipe', () => {
|
||||
var localization: NgLocalization;
|
||||
var pipe: I18nPluralPipe;
|
||||
let localization: NgLocalization;
|
||||
let pipe: I18nPluralPipe;
|
||||
|
||||
var mapping = {
|
||||
const mapping = {
|
||||
'=0': 'No messages.',
|
||||
'=1': 'One message.',
|
||||
'many': 'Many messages.',
|
||||
@ -32,27 +32,27 @@ export function main() {
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return 0 text if value is 0', () => {
|
||||
var val = pipe.transform(0, mapping);
|
||||
const val = pipe.transform(0, mapping);
|
||||
expect(val).toEqual('No messages.');
|
||||
});
|
||||
|
||||
it('should return 1 text if value is 1', () => {
|
||||
var val = pipe.transform(1, mapping);
|
||||
const val = pipe.transform(1, mapping);
|
||||
expect(val).toEqual('One message.');
|
||||
});
|
||||
|
||||
it('should return category messages', () => {
|
||||
var val = pipe.transform(4, mapping);
|
||||
const val = pipe.transform(4, mapping);
|
||||
expect(val).toEqual('Many messages.');
|
||||
});
|
||||
|
||||
it('should interpolate the value into the text where indicated', () => {
|
||||
var val = pipe.transform(6, mapping);
|
||||
const val = pipe.transform(6, mapping);
|
||||
expect(val).toEqual('There are 6 messages, that is 6.');
|
||||
});
|
||||
|
||||
it('should use "" if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
const val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
|
@ -8,40 +8,35 @@
|
||||
|
||||
import {I18nSelectPipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nSelectPipe', () => {
|
||||
var pipe: I18nSelectPipe;
|
||||
var mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||
|
||||
beforeEach(() => { pipe = new I18nSelectPipe(); });
|
||||
const pipe: I18nSelectPipe = new I18nSelectPipe();
|
||||
const mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return male text if value is male', () => {
|
||||
var val = pipe.transform('male', mapping);
|
||||
it('should return the "male" text if value is "male"', () => {
|
||||
const val = pipe.transform('male', mapping);
|
||||
expect(val).toEqual('Invite him.');
|
||||
});
|
||||
|
||||
it('should return female text if value is female', () => {
|
||||
var val = pipe.transform('female', mapping);
|
||||
it('should return the "female" text if value is "female"', () => {
|
||||
const val = pipe.transform('female', mapping);
|
||||
expect(val).toEqual('Invite her.');
|
||||
});
|
||||
|
||||
it('should return "" if value is anything other than male or female', () => {
|
||||
var val = pipe.transform('Anything else', mapping);
|
||||
expect(val).toEqual('');
|
||||
it('should return the "other" text if value is neither "male" nor "female"',
|
||||
() => { expect(pipe.transform('Anything else', mapping)).toEqual('Invite them.'); });
|
||||
|
||||
it('should return an empty text if value is null or undefined', () => {
|
||||
expect(pipe.transform(null, mapping)).toEqual('');
|
||||
expect(pipe.transform(void 0, mapping)).toEqual('');
|
||||
});
|
||||
|
||||
it('should use "" if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
it('should not support bad arguments',
|
||||
it('should throw on bad arguments',
|
||||
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
||||
});
|
||||
|
||||
|
@ -13,10 +13,10 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('JsonPipe', () => {
|
||||
var regNewLine = '\n';
|
||||
var inceptionObj: any;
|
||||
var inceptionObjString: string;
|
||||
var pipe: JsonPipe;
|
||||
const regNewLine = '\n';
|
||||
let inceptionObj: any;
|
||||
let inceptionObjString: string;
|
||||
let pipe: JsonPipe;
|
||||
|
||||
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
||||
|
||||
@ -39,14 +39,14 @@ export function main() {
|
||||
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
||||
|
||||
it('should return JSON-formatted string even when normalized', () => {
|
||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||
var dream2 = normalize(inceptionObjString);
|
||||
const dream1 = normalize(pipe.transform(inceptionObj));
|
||||
const dream2 = normalize(inceptionObjString);
|
||||
expect(dream1).toEqual(dream2);
|
||||
});
|
||||
|
||||
it('should return JSON-formatted string similar to Json.stringify', () => {
|
||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||
var dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
||||
const dream1 = normalize(pipe.transform(inceptionObj));
|
||||
const dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
||||
expect(dream1).toEqual(dream2);
|
||||
});
|
||||
});
|
||||
@ -63,8 +63,8 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should work with mutable objects', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComp);
|
||||
let mutable: number[] = [1];
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
const mutable: number[] = [1];
|
||||
fixture.componentInstance.data = mutable;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
|
||||
|
@ -11,9 +11,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
||||
|
||||
export function main() {
|
||||
describe('LowerCasePipe', () => {
|
||||
var upper: string;
|
||||
var lower: string;
|
||||
var pipe: LowerCasePipe;
|
||||
let upper: string;
|
||||
let lower: string;
|
||||
let pipe: LowerCasePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
lower = 'something';
|
||||
@ -23,14 +23,14 @@ export function main() {
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return lowercase', () => {
|
||||
var val = pipe.transform(upper);
|
||||
const val = pipe.transform(upper);
|
||||
expect(val).toEqual(lower);
|
||||
});
|
||||
|
||||
it('should lowercase when there is a new value', () => {
|
||||
var val = pipe.transform(upper);
|
||||
const val = pipe.transform(upper);
|
||||
expect(val).toEqual(lower);
|
||||
var val2 = pipe.transform('WAT');
|
||||
const val2 = pipe.transform('WAT');
|
||||
expect(val2).toEqual('wat');
|
||||
});
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
export function main() {
|
||||
describe('Number pipes', () => {
|
||||
describe('DecimalPipe', () => {
|
||||
var pipe: DecimalPipe;
|
||||
let pipe: DecimalPipe;
|
||||
|
||||
beforeEach(() => { pipe = new DecimalPipe('en-US'); });
|
||||
|
||||
@ -44,7 +44,7 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('PercentPipe', () => {
|
||||
var pipe: PercentPipe;
|
||||
let pipe: PercentPipe;
|
||||
|
||||
beforeEach(() => { pipe = new PercentPipe('en-US'); });
|
||||
|
||||
@ -60,7 +60,7 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('CurrencyPipe', () => {
|
||||
var pipe: CurrencyPipe;
|
||||
let pipe: CurrencyPipe;
|
||||
|
||||
beforeEach(() => { pipe = new CurrencyPipe('en-US'); });
|
||||
|
||||
|
@ -13,9 +13,9 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('SlicePipe', () => {
|
||||
var list: number[];
|
||||
var str: string;
|
||||
var pipe: SlicePipe;
|
||||
let list: number[];
|
||||
let str: string;
|
||||
let pipe: SlicePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
list = [1, 2, 3, 4, 5];
|
||||
@ -93,8 +93,8 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should work with mutable arrays', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComp);
|
||||
let mutable: number[] = [1, 2];
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
const mutable: number[] = [1, 2];
|
||||
fixture.componentInstance.data = mutable;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('2');
|
||||
|
@ -11,9 +11,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
||||
|
||||
export function main() {
|
||||
describe('UpperCasePipe', () => {
|
||||
var upper: string;
|
||||
var lower: string;
|
||||
var pipe: UpperCasePipe;
|
||||
let upper: string;
|
||||
let lower: string;
|
||||
let pipe: UpperCasePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
lower = 'something';
|
||||
@ -24,14 +24,14 @@ export function main() {
|
||||
describe('transform', () => {
|
||||
|
||||
it('should return uppercase', () => {
|
||||
var val = pipe.transform(lower);
|
||||
const val = pipe.transform(lower);
|
||||
expect(val).toEqual(upper);
|
||||
});
|
||||
|
||||
it('should uppercase when there is a new value', () => {
|
||||
var val = pipe.transform(lower);
|
||||
const val = pipe.transform(lower);
|
||||
expect(val).toEqual(upper);
|
||||
var val2 = pipe.transform('wat');
|
||||
const val2 = pipe.transform('wat');
|
||||
expect(val2).toEqual('WAT');
|
||||
});
|
||||
|
||||
|
@ -34,8 +34,8 @@ export class SpyLocation implements Location {
|
||||
path(): string { return this._history[this._historyIndex].path; }
|
||||
|
||||
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||
var givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||
var currPath =
|
||||
const givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||
const currPath =
|
||||
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
||||
|
||||
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
||||
@ -66,12 +66,12 @@ export class SpyLocation implements Location {
|
||||
this._history.push(new LocationState(path, query));
|
||||
this._historyIndex = this._history.length - 1;
|
||||
|
||||
var locationState = this._history[this._historyIndex - 1];
|
||||
const locationState = this._history[this._historyIndex - 1];
|
||||
if (locationState.path == path && locationState.query == query) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.urlChanges.push(url);
|
||||
this._subject.emit({'url': url, 'pop': false});
|
||||
}
|
||||
@ -79,7 +79,7 @@ export class SpyLocation implements Location {
|
||||
replaceState(path: string, query: string = '') {
|
||||
path = this.prepareExternalUrl(path);
|
||||
|
||||
var history = this._history[this._historyIndex];
|
||||
const history = this._history[this._historyIndex];
|
||||
if (history.path == path && history.query == query) {
|
||||
return;
|
||||
}
|
||||
@ -87,7 +87,7 @@ export class SpyLocation implements Location {
|
||||
history.path = path;
|
||||
history.query = query;
|
||||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.urlChanges.push('replace: ' + url);
|
||||
}
|
||||
|
||||
|
@ -44,20 +44,20 @@ export class MockLocationStrategy extends LocationStrategy {
|
||||
pushState(ctx: any, title: string, path: string, query: string): void {
|
||||
this.internalTitle = title;
|
||||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.internalPath = url;
|
||||
|
||||
var externalUrl = this.prepareExternalUrl(url);
|
||||
const externalUrl = this.prepareExternalUrl(url);
|
||||
this.urlChanges.push(externalUrl);
|
||||
}
|
||||
|
||||
replaceState(ctx: any, title: string, path: string, query: string): void {
|
||||
this.internalTitle = title;
|
||||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.internalPath = url;
|
||||
|
||||
var externalUrl = this.prepareExternalUrl(url);
|
||||
const externalUrl = this.prepareExternalUrl(url);
|
||||
this.urlChanges.push('replace: ' + externalUrl);
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ export class MockLocationStrategy extends LocationStrategy {
|
||||
back(): void {
|
||||
if (this.urlChanges.length > 0) {
|
||||
this.urlChanges.pop();
|
||||
var nextUrl = this.urlChanges.length > 0 ? this.urlChanges[this.urlChanges.length - 1] : '';
|
||||
const nextUrl = this.urlChanges.length > 0 ? this.urlChanges[this.urlChanges.length - 1] : '';
|
||||
this.simulatePopState(nextUrl);
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
* 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, 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';
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {ApplicationRef, NgModule} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {ServerModule} from '@angular/platform-server';
|
||||
import {MdButtonModule} from '@angular2-material/button';
|
||||
|
||||
import {ThirdpartyModule} from '../third_party_src/module';
|
||||
@ -48,7 +48,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
||||
ComponentUsingThirdParty,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
ServerModule,
|
||||
FormsModule,
|
||||
MdButtonModule,
|
||||
ModuleUsingCustomElements,
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
import {LowerCasePipe, NgIf} from '@angular/common';
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
@Injectable()
|
||||
export class SomeService {
|
||||
|
@ -19,9 +19,9 @@ describe('template codegen output', () => {
|
||||
|
||||
it('should apply the animate states to the element', (done) => {
|
||||
const compFixture = createComponent(AnimateCmp);
|
||||
var debugElement = compFixture.debugElement;
|
||||
const debugElement = compFixture.debugElement;
|
||||
|
||||
var targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||
const targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||
|
||||
compFixture.componentInstance.setAsOpen();
|
||||
compFixture.detectChanges();
|
||||
@ -45,9 +45,9 @@ describe('template codegen output', () => {
|
||||
|
||||
it('should apply the default animate state to the element', (done) => {
|
||||
const compFixture = createComponent(AnimateCmp);
|
||||
var debugElement = compFixture.debugElement;
|
||||
const debugElement = compFixture.debugElement;
|
||||
|
||||
var targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||
const targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||
|
||||
compFixture.componentInstance.setAsSomethingElse();
|
||||
compFixture.detectChanges();
|
||||
|
@ -15,8 +15,8 @@ import {createComponent} from './util';
|
||||
|
||||
describe('content projection', () => {
|
||||
it('should support entryComponents in components', () => {
|
||||
var compFixture = createComponent(CompWithEntryComponents);
|
||||
var cf = compFixture.componentInstance.cfr.resolveComponentFactory(BasicComp);
|
||||
const compFixture = createComponent(CompWithEntryComponents);
|
||||
const cf = compFixture.componentInstance.cfr.resolveComponentFactory(BasicComp);
|
||||
expect(cf.componentType).toBe(BasicComp);
|
||||
});
|
||||
|
||||
|
@ -13,10 +13,10 @@ import {createComponent} from './util';
|
||||
|
||||
describe('content projection', () => {
|
||||
it('should support basic content projection', () => {
|
||||
var mainCompFixture = createComponent(ProjectingComp);
|
||||
const mainCompFixture = createComponent(ProjectingComp);
|
||||
|
||||
var debugElement = mainCompFixture.debugElement;
|
||||
var compWithProjection = debugElement.query(By.directive(CompWithNgContent));
|
||||
const debugElement = mainCompFixture.debugElement;
|
||||
const compWithProjection = debugElement.query(By.directive(CompWithNgContent));
|
||||
expect(compWithProjection.children.length).toBe(1);
|
||||
expect(compWithProjection.children[0].attributes['greeting']).toEqual('Hello world!');
|
||||
});
|
||||
|
@ -14,18 +14,18 @@ import {createComponent} from './util';
|
||||
|
||||
describe('child queries', () => {
|
||||
it('should support compiling child queries', () => {
|
||||
var childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||
var debugElement = childQueryCompFixture.debugElement;
|
||||
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||
const childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||
const debugElement = childQueryCompFixture.debugElement;
|
||||
const compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||
expect(childQueryCompFixture.componentInstance.child).toBeDefined();
|
||||
expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
it('should support compiling children queries', () => {
|
||||
var childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||
var debugElement = childQueryCompFixture.debugElement;
|
||||
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||
const childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||
const debugElement = childQueryCompFixture.debugElement;
|
||||
const compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||
|
||||
childQueryCompFixture.detectChanges();
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "^0.3.0",
|
||||
"@angular/tsc-wrapped": "^0.4.0",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -13,23 +13,21 @@
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
||||
import {readFileSync} from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {PathMappedReflectorHost} from './path_mapped_reflector_host';
|
||||
import {CompilerHost, CompilerHostContext} 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 nodeFs = require('fs');
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
|
||||
const PREAMBLE = `/**
|
||||
* This file is generated by the Angular 2 template compiler.
|
||||
* @fileoverview This file is generated by the Angular 2 template compiler.
|
||||
* Do not edit.
|
||||
* @suppress {suspiciousCode,uselessCode,missingProperties}
|
||||
*/
|
||||
/* tslint:disable */
|
||||
|
||||
@ -38,8 +36,8 @@ 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 {
|
||||
@ -64,33 +62,30 @@ 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.fileUrl);
|
||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
||||
this.host.writeFile(
|
||||
emitPath, PREAMBLE + generatedModule.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;
|
||||
ngCompilerHost = usePathMapping ?
|
||||
new PathMappedCompilerHost(program, tsCompilerHost, options, compilerHostContext) :
|
||||
new CompilerHost(program, tsCompilerHost, options, compilerHostContext);
|
||||
}
|
||||
const transFile = cliOptions.i18nFile;
|
||||
const locale = cliOptions.locale;
|
||||
let transContent: string = '';
|
||||
@ -99,85 +94,20 @@ export class CodeGenerator {
|
||||
throw new Error(
|
||||
`The translation file (${transFile}) locale must be provided. Use the --locale option.`);
|
||||
}
|
||||
transContent = nodeFs.readFileSync(transFile, 'utf8');
|
||||
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, staticReflector);
|
||||
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
resolver, normalizer, 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
247
modules/@angular/compiler-cli/src/compiler_host.ts
Normal file
247
modules/@angular/compiler-cli/src/compiler_host.ts
Normal file
@ -0,0 +1,247 @@
|
||||
/**
|
||||
* @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 {
|
||||
fileExists(fileName: string): boolean;
|
||||
directoryExists(directoryName: string): boolean;
|
||||
readFile(fileName: string): string;
|
||||
readResource(fileName: string): Promise<string>;
|
||||
assumeFileExists(fileName: string): void;
|
||||
}
|
||||
|
||||
export class CompilerHost implements AotCompilerHost {
|
||||
protected metadataCollector = new MetadataCollector();
|
||||
protected context: CompilerHostContext;
|
||||
private isGenDirChildOfRootDir: boolean;
|
||||
protected basePath: string;
|
||||
private genDir: string;
|
||||
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||
|
||||
constructor(
|
||||
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
|
||||
protected options: AngularCompilerOptions, 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, '/');
|
||||
|
||||
this.context = context || new NodeCompilerHostContext(compilerHost);
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
||||
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 => m['version'] === 1);
|
||||
let v2Metadata = metadatas.find(m => 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
|
||||
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); }
|
||||
}
|
||||
|
||||
export class NodeCompilerHostContext implements CompilerHostContext {
|
||||
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'); }
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -14,97 +14,28 @@
|
||||
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} 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 directiveNormalizer: compiler.DirectiveNormalizer) {}
|
||||
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);
|
||||
|
||||
const files =
|
||||
compiler.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
|
||||
.files;
|
||||
const errors: compiler.ParseError[] = [];
|
||||
const filePromises: Promise<any>[] = [];
|
||||
|
||||
files.forEach(file => {
|
||||
const cmpPromises: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
||||
file.directives.forEach(directiveType => {
|
||||
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
||||
if (dirMeta.isComponent) {
|
||||
cmpPromises.push(this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult);
|
||||
}
|
||||
});
|
||||
|
||||
if (cmpPromises.length) {
|
||||
const done =
|
||||
Promise.all(cmpPromises).then((compMetas: compiler.CompileDirectiveMetadata[]) => {
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
filePromises.push(done);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||
}
|
||||
|
||||
return Promise.all(filePromises).then(_ => 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, staticReflector);
|
||||
|
||||
// TODO(vicb): implicit tags & attributes
|
||||
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
||||
|
||||
return new Extractor(
|
||||
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver,
|
||||
normalizer);
|
||||
tsCompilerHost: ts.CompilerHost, ngCompilerHost?: CompilerHost): Extractor {
|
||||
if (!ngCompilerHost) ngCompilerHost = new CompilerHost(program, tsCompilerHost, options);
|
||||
const {extractor: ngExtractor} = compiler.Extractor.create(
|
||||
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
|
||||
return new Extractor(ngExtractor, ngCompilerHost, program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,7 @@ 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();
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
|
@ -6,35 +6,35 @@
|
||||
* 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 {
|
||||
export class PathMappedCompilerHost extends CompilerHost {
|
||||
constructor(
|
||||
program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions,
|
||||
context?: ReflectorHostContext) {
|
||||
context?: CompilerHostContext) {
|
||||
super(program, compilerHost, options, context);
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
if (!fileName) return fileName;
|
||||
// NB: the rootDirs should have been sorted longest-first
|
||||
for (let dir of this.options.rootDirs || []) {
|
||||
for (const dir of this.options.rootDirs || []) {
|
||||
if (fileName.indexOf(dir) === 0) {
|
||||
fileName = fileName.substring(dir.length);
|
||||
}
|
||||
@ -42,7 +42,14 @@ 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 =
|
||||
@ -51,7 +58,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
||||
if (this.options.traceResolution) {
|
||||
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||
}
|
||||
return resolved.resolvedFileName;
|
||||
return this.getCanonicalFileName(resolved.resolvedFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,10 +69,7 @@ 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(
|
||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||
@ -82,11 +86,11 @@ 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, '');
|
||||
};
|
||||
|
||||
let importModuleName = importedFile.replace(EXT, '');
|
||||
const importModuleName = importedFile.replace(EXT, '');
|
||||
const parts = importModuleName.split(path.sep).filter(p => !!p);
|
||||
let foundRelativeImport: string;
|
||||
for (let index = parts.length - 1; index >= 0; index--) {
|
||||
@ -112,7 +116,7 @@ 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)) {
|
||||
@ -124,16 +128,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);
|
||||
var 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 {
|
||||
let assetUrl = AssetUrl.parse(url);
|
||||
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
||||
return this.getCanonicalFileName(path);
|
||||
}
|
||||
|
||||
protected resolveAssetUrl(url: string, containingFile: string): string {
|
||||
let 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, '');
|
||||
|
||||
var 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 {
|
||||
var 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) {
|
||||
var 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 {
|
||||
let 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;
|
||||
};
|
||||
let 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; }
|
||||
}
|
219
modules/@angular/compiler-cli/test/aot_host_spec.ts
Normal file
219
modules/@angular/compiler-cli/test/aot_host_spec.ts
Normal file
@ -0,0 +1,219 @@
|
||||
/**
|
||||
* @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 host: ts.CompilerHost;
|
||||
let program: ts.Program;
|
||||
let hostNestedGenDir: CompilerHost;
|
||||
let hostSiblingGenDir: CompilerHost;
|
||||
|
||||
beforeEach(() => {
|
||||
context = new MockAotContext('/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');
|
||||
}
|
||||
hostNestedGenDir = new CompilerHost(
|
||||
program, host, {
|
||||
genDir: '/tmp/project/src/gen/',
|
||||
basePath: '/tmp/project/src',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
hostSiblingGenDir = new CompilerHost(
|
||||
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(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'}}
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
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 {}',
|
||||
'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;
|
||||
}
|
||||
}
|
@ -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'; }
|
||||
@ -21,17 +21,25 @@ export class MockContext implements ReflectorHostContext {
|
||||
directoryExists(path: string): boolean { return typeof this.getEntry(path) === 'object'; }
|
||||
|
||||
readFile(fileName: string): string|undefined {
|
||||
let data = this.getEntry(fileName);
|
||||
const data = this.getEntry(fileName);
|
||||
if (typeof data === 'string') {
|
||||
return data;
|
||||
}
|
||||
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 {
|
||||
let parts = fileName.split('/');
|
||||
let name = parts.pop();
|
||||
let entry = this.getEntry(parts);
|
||||
const parts = fileName.split('/');
|
||||
const name = parts.pop();
|
||||
const entry = this.getEntry(parts);
|
||||
if (entry && typeof entry !== 'string') {
|
||||
entry[name] = data;
|
||||
}
|
||||
@ -48,11 +56,11 @@ export class MockContext implements ReflectorHostContext {
|
||||
parts = normalize(parts);
|
||||
let current = this.files;
|
||||
while (parts.length) {
|
||||
let part = parts.shift();
|
||||
const part = parts.shift();
|
||||
if (typeof current === 'string') {
|
||||
return undefined;
|
||||
}
|
||||
let next = (<Directory>current)[part];
|
||||
const next = (<Directory>current)[part];
|
||||
if (next === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@ -72,9 +80,9 @@ export class MockContext implements ReflectorHostContext {
|
||||
}
|
||||
|
||||
function normalize(parts: string[]): string[] {
|
||||
let result: string[] = [];
|
||||
const result: string[] = [];
|
||||
while (parts.length) {
|
||||
let part = parts.shift();
|
||||
const part = parts.shift();
|
||||
switch (part) {
|
||||
case '.':
|
||||
break;
|
||||
@ -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); }
|
||||
|
||||
@ -102,7 +110,7 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||
getSourceFile(
|
||||
fileName: string, languageVersion: ts.ScriptTarget,
|
||||
onError?: (message: string) => void): ts.SourceFile {
|
||||
let sourceText = this.context.readFile(fileName);
|
||||
const sourceText = this.context.readFile(fileName);
|
||||
if (sourceText) {
|
||||
return ts.createSourceFile(fileName, sourceText, languageVersion);
|
||||
} else {
|
||||
|
@ -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', () => {
|
||||
var context: MockContext;
|
||||
var host: ts.CompilerHost;
|
||||
var program: ts.Program;
|
||||
var reflectorNestedGenDir: ReflectorHost;
|
||||
var 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
|
||||
let 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', () => {
|
||||
let {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', () => {
|
||||
let foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
||||
let 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 {
|
||||
let result: Directory = {};
|
||||
for (let name in entry) {
|
||||
result[name] = clone(entry[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -25,11 +25,16 @@ 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 {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';
|
||||
@ -52,5 +57,6 @@ export * from './src/selector';
|
||||
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.
|
||||
|
@ -29,19 +29,21 @@ export class AnimationCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
var _ANIMATION_FACTORY_ELEMENT_VAR = o.variable('element');
|
||||
var _ANIMATION_DEFAULT_STATE_VAR = o.variable('defaultStateStyles');
|
||||
var _ANIMATION_FACTORY_VIEW_VAR = o.variable('view');
|
||||
var _ANIMATION_FACTORY_VIEW_CONTEXT = _ANIMATION_FACTORY_VIEW_VAR.prop('animationContext');
|
||||
var _ANIMATION_FACTORY_RENDERER_VAR = _ANIMATION_FACTORY_VIEW_VAR.prop('renderer');
|
||||
var _ANIMATION_CURRENT_STATE_VAR = o.variable('currentState');
|
||||
var _ANIMATION_NEXT_STATE_VAR = o.variable('nextState');
|
||||
var _ANIMATION_PLAYER_VAR = o.variable('player');
|
||||
var _ANIMATION_TIME_VAR = o.variable('totalTime');
|
||||
var _ANIMATION_START_STATE_STYLES_VAR = o.variable('startStateStyles');
|
||||
var _ANIMATION_END_STATE_STYLES_VAR = o.variable('endStateStyles');
|
||||
var _ANIMATION_COLLECTED_STYLES = o.variable('collectedStyles');
|
||||
var EMPTY_MAP = o.literalMap([]);
|
||||
const _ANIMATION_FACTORY_ELEMENT_VAR = o.variable('element');
|
||||
const _ANIMATION_DEFAULT_STATE_VAR = o.variable('defaultStateStyles');
|
||||
const _ANIMATION_FACTORY_VIEW_VAR = o.variable('view');
|
||||
const _ANIMATION_FACTORY_VIEW_CONTEXT = _ANIMATION_FACTORY_VIEW_VAR.prop('animationContext');
|
||||
const _ANIMATION_FACTORY_RENDERER_VAR = _ANIMATION_FACTORY_VIEW_VAR.prop('renderer');
|
||||
const _ANIMATION_CURRENT_STATE_VAR = o.variable('currentState');
|
||||
const _ANIMATION_NEXT_STATE_VAR = o.variable('nextState');
|
||||
const _ANIMATION_PLAYER_VAR = o.variable('player');
|
||||
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 _PREVIOUS_ANIMATION_PLAYERS = o.variable('previousPlayers');
|
||||
const _EMPTY_MAP = o.literalMap([]);
|
||||
const _EMPTY_ARRAY = o.literalArr([]);
|
||||
|
||||
class _AnimationBuilder implements AnimationAstVisitor {
|
||||
private _fnVarName: string;
|
||||
@ -55,7 +57,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
}
|
||||
|
||||
visitAnimationStyles(ast: AnimationStylesAst, context: _AnimationBuilderContext): o.Expression {
|
||||
var stylesArr: any[] = [];
|
||||
const stylesArr: any[] = [];
|
||||
if (context.isExpectingFirstStyleStep) {
|
||||
stylesArr.push(_ANIMATION_START_STATE_STYLES_VAR);
|
||||
context.isExpectingFirstStyleStep = false;
|
||||
@ -86,8 +88,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
return this._visitEndStateAnimation(ast, context);
|
||||
}
|
||||
|
||||
var startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
var keyframeExpressions =
|
||||
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
const keyframeExpressions =
|
||||
ast.keyframes.map(keyframeEntry => keyframeEntry.visit(this, context));
|
||||
return this._callAnimateMethod(
|
||||
ast, startingStylesExpr, o.literalArr(keyframeExpressions), context);
|
||||
@ -95,9 +97,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
|
||||
/** @internal */
|
||||
_visitEndStateAnimation(ast: AnimationStepAst, context: _AnimationBuilderContext): o.Expression {
|
||||
var startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
var keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
||||
var keyframesExpr =
|
||||
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([
|
||||
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
||||
o.literalArr(keyframeExpressions)
|
||||
@ -110,23 +112,28 @@ 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 {
|
||||
var playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
|
||||
o.literalArr(playerExprs)
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
||||
var playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
|
||||
o.literalArr(playerExprs)
|
||||
]);
|
||||
@ -134,7 +141,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
|
||||
visitAnimationStateDeclaration(
|
||||
ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
|
||||
var flatStyles: {[key: string]: string | number} = {};
|
||||
const flatStyles: {[key: string]: string | number} = {};
|
||||
_getStylesArray(ast).forEach(
|
||||
entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); });
|
||||
context.stateMap.registerState(ast.stateName, flatStyles);
|
||||
@ -142,16 +149,17 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
|
||||
visitAnimationStateTransition(
|
||||
ast: AnimationStateTransitionAst, context: _AnimationBuilderContext): any {
|
||||
var steps = ast.animation.steps;
|
||||
var lastStep = steps[steps.length - 1];
|
||||
const steps = ast.animation.steps;
|
||||
const lastStep = steps[steps.length - 1];
|
||||
if (_isEndStateAnimateStep(lastStep)) {
|
||||
context.endStateAnimateStep = <AnimationStepAst>lastStep;
|
||||
}
|
||||
|
||||
context.totalTransitionTime = 0;
|
||||
context.isExpectingFirstStyleStep = true;
|
||||
context.isExpectingFirstAnimateStep = true;
|
||||
|
||||
var stateChangePreconditions: o.Expression[] = [];
|
||||
const stateChangePreconditions: o.Expression[] = [];
|
||||
|
||||
ast.stateChanges.forEach(stateChange => {
|
||||
stateChangePreconditions.push(
|
||||
@ -167,14 +175,14 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
}
|
||||
});
|
||||
|
||||
var animationPlayerExpr = ast.animation.visit(this, context);
|
||||
const animationPlayerExpr = ast.animation.visit(this, context);
|
||||
|
||||
var reducedStateChangesPrecondition = stateChangePreconditions.reduce((a, b) => a.or(b));
|
||||
var precondition =
|
||||
const reducedStateChangesPrecondition = stateChangePreconditions.reduce((a, b) => a.or(b));
|
||||
const precondition =
|
||||
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR).and(reducedStateChangesPrecondition);
|
||||
|
||||
var animationStmt = _ANIMATION_PLAYER_VAR.set(animationPlayerExpr).toStmt();
|
||||
var totalTimeStmt = _ANIMATION_TIME_VAR.set(o.literal(context.totalTransitionTime)).toStmt();
|
||||
const animationStmt = _ANIMATION_PLAYER_VAR.set(animationPlayerExpr).toStmt();
|
||||
const totalTimeStmt = _ANIMATION_TIME_VAR.set(o.literal(context.totalTransitionTime)).toStmt();
|
||||
|
||||
return new o.IfStmt(precondition, [animationStmt, totalTimeStmt]);
|
||||
}
|
||||
@ -186,18 +194,17 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
// this should always be defined even if the user overrides it
|
||||
context.stateMap.registerState(DEFAULT_STATE, {});
|
||||
|
||||
var statements: o.Statement[] = [];
|
||||
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
|
||||
.callMethod(
|
||||
'cancelActiveAnimation',
|
||||
const statements: o.Statement[] = [];
|
||||
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()]));
|
||||
|
||||
var 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(resolveIdentifier(Identifiers.renderStyles));
|
||||
|
||||
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
||||
|
||||
@ -251,19 +247,40 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
_ANIMATION_PLAYER_VAR
|
||||
.callMethod(
|
||||
'onDone',
|
||||
[o.fn(
|
||||
[],
|
||||
[RENDER_STYLES_FN
|
||||
.callFn([
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||
[o
|
||||
.fn([],
|
||||
[
|
||||
_ANIMATION_PLAYER_VAR.callMethod('destroy', []).toStmt(),
|
||||
RENDER_STYLES_FN
|
||||
.callFn([
|
||||
_ANIMATION_START_STATE_STYLES_VAR, _ANIMATION_END_STATE_STYLES_VAR
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(
|
||||
resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||
.callFn([
|
||||
_ANIMATION_START_STATE_STYLES_VAR,
|
||||
_ANIMATION_END_STATE_STYLES_VAR
|
||||
])
|
||||
])
|
||||
])
|
||||
.toStmt()])])
|
||||
.toStmt()
|
||||
])])
|
||||
.toStmt());
|
||||
|
||||
statements.push(o.importExpr(resolveIdentifier(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(resolveIdentifier(Identifiers.clearStyles))
|
||||
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
||||
])
|
||||
.toStmt());
|
||||
|
||||
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
|
||||
.callMethod(
|
||||
'queueAnimation',
|
||||
@ -292,16 +309,16 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
}
|
||||
|
||||
build(ast: AnimationAst): AnimationEntryCompileResult {
|
||||
var context = new _AnimationBuilderContext();
|
||||
var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
|
||||
var fnVariable = o.variable(this._fnVarName);
|
||||
const context = new _AnimationBuilderContext();
|
||||
const fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
|
||||
const fnVariable = o.variable(this._fnVarName);
|
||||
|
||||
var lookupMap: any[] = [];
|
||||
const lookupMap: any[] = [];
|
||||
Object.keys(context.stateMap.states).forEach(stateName => {
|
||||
const value = context.stateMap.states[stateName];
|
||||
var variableValue = EMPTY_MAP;
|
||||
let variableValue = _EMPTY_MAP;
|
||||
if (isPresent(value)) {
|
||||
let styleMap: any[] = [];
|
||||
const styleMap: any[] = [];
|
||||
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
|
||||
variableValue = o.literalMap(styleMap);
|
||||
}
|
||||
@ -319,6 +336,7 @@ class _AnimationBuilderContext {
|
||||
stateMap = new _AnimationBuilderStateMap();
|
||||
endStateAnimateStep: AnimationStepAst = null;
|
||||
isExpectingFirstStyleStep = false;
|
||||
isExpectingFirstAnimateStep = false;
|
||||
totalTransitionTime = 0;
|
||||
}
|
||||
|
||||
@ -326,7 +344,7 @@ class _AnimationBuilderStateMap {
|
||||
private _states: {[key: string]: {[prop: string]: string | number}} = {};
|
||||
get states() { return this._states; }
|
||||
registerState(name: string, value: {[prop: string]: string | number} = null): void {
|
||||
var existingEntry = this._states[name];
|
||||
const existingEntry = this._states[name];
|
||||
if (!existingEntry) {
|
||||
this._states[name] = value;
|
||||
}
|
||||
@ -334,7 +352,7 @@ class _AnimationBuilderStateMap {
|
||||
}
|
||||
|
||||
function _compareToAnimationStateExpr(value: o.Expression, animationState: string): o.Expression {
|
||||
var emptyStateLiteral = o.literal(EMPTY_STATE);
|
||||
const emptyStateLiteral = o.literal(EMPTY_STATE);
|
||||
switch (animationState) {
|
||||
case EMPTY_STATE:
|
||||
return value.equals(emptyStateLiteral);
|
||||
@ -351,8 +369,8 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
|
||||
// the final animation step is characterized by having only TWO
|
||||
// keyframe values and it must have zero styles for both keyframes
|
||||
if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) {
|
||||
var styles1 = _getStylesArray(step.keyframes[0])[0];
|
||||
var styles2 = _getStylesArray(step.keyframes[1])[0];
|
||||
const styles1 = _getStylesArray(step.keyframes[0])[0];
|
||||
const styles2 = _getStylesArray(step.keyframes[1])[0];
|
||||
return Object.keys(styles1).length === 0 && Object.keys(styles2).length === 0;
|
||||
}
|
||||
return false;
|
||||
|
@ -6,11 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
|
||||
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
||||
import {StylesCollection} from './styles_collection';
|
||||
@ -32,7 +35,10 @@ export class AnimationEntryParseResult {
|
||||
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AnimationParser {
|
||||
constructor(private _schema: ElementSchemaRegistry) {}
|
||||
|
||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||
const errors: string[] = [];
|
||||
const componentName = component.type.name;
|
||||
@ -66,14 +72,14 @@ export class AnimationParser {
|
||||
}
|
||||
|
||||
parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult {
|
||||
var errors: AnimationParseError[] = [];
|
||||
var stateStyles: {[key: string]: AnimationStylesAst} = {};
|
||||
var transitions: CompileAnimationStateTransitionMetadata[] = [];
|
||||
const errors: AnimationParseError[] = [];
|
||||
const stateStyles: {[key: string]: AnimationStylesAst} = {};
|
||||
const transitions: CompileAnimationStateTransitionMetadata[] = [];
|
||||
|
||||
var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
||||
const stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
||||
entry.definitions.forEach(def => {
|
||||
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
||||
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
|
||||
_parseAnimationDeclarationStates(def, this._schema, errors).forEach(ast => {
|
||||
stateDeclarationAsts.push(ast);
|
||||
stateStyles[ast.stateName] = ast.styles;
|
||||
});
|
||||
@ -82,50 +88,40 @@ export class AnimationParser {
|
||||
}
|
||||
});
|
||||
|
||||
var stateTransitionAsts =
|
||||
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
|
||||
const stateTransitionAsts = transitions.map(
|
||||
transDef => _parseAnimationStateTransition(transDef, stateStyles, this._schema, errors));
|
||||
|
||||
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||
const ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||
return new AnimationEntryParseResult(ast, errors);
|
||||
}
|
||||
}
|
||||
|
||||
function _parseAnimationDeclarationStates(
|
||||
stateMetadata: CompileAnimationStateDeclarationMetadata,
|
||||
stateMetadata: CompileAnimationStateDeclarationMetadata, schema: ElementSchemaRegistry,
|
||||
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
||||
var styleValues: Styles[] = [];
|
||||
stateMetadata.styles.styles.forEach(stylesEntry => {
|
||||
// TODO (matsko): change this when we get CSS class integration support
|
||||
if (typeof stylesEntry === 'object' && stylesEntry !== null) {
|
||||
styleValues.push(stylesEntry as Styles);
|
||||
} else {
|
||||
errors.push(new AnimationParseError(
|
||||
`State based animations cannot contain references to other states`));
|
||||
}
|
||||
});
|
||||
var defStyles = new AnimationStylesAst(styleValues);
|
||||
|
||||
var states = stateMetadata.stateNameExpr.split(/\s*,\s*/);
|
||||
const normalizedStyles = _normalizeStyleMetadata(stateMetadata.styles, {}, schema, errors, false);
|
||||
const defStyles = new AnimationStylesAst(normalizedStyles);
|
||||
const states = stateMetadata.stateNameExpr.split(/\s*,\s*/);
|
||||
return states.map(state => new AnimationStateDeclarationAst(state, defStyles));
|
||||
}
|
||||
|
||||
function _parseAnimationStateTransition(
|
||||
transitionStateMetadata: CompileAnimationStateTransitionMetadata,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
stateStyles: {[key: string]: AnimationStylesAst}, schema: ElementSchemaRegistry,
|
||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||
var styles = new StylesCollection();
|
||||
var transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
||||
const styles = new StylesCollection();
|
||||
const transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||
const transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
||||
transitionStates.forEach(
|
||||
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
||||
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
||||
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||
const entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||
const animation = _normalizeStyleSteps(entry, stateStyles, schema, errors);
|
||||
const animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||
if (errors.length == 0) {
|
||||
_fillAnimationAstStartingKeyframes(animationAst, styles, errors);
|
||||
}
|
||||
|
||||
var stepsAst: AnimationWithStepsAst = (animationAst instanceof AnimationWithStepsAst) ?
|
||||
const stepsAst: AnimationWithStepsAst = (animationAst instanceof AnimationWithStepsAst) ?
|
||||
animationAst :
|
||||
new AnimationSequenceAst([animationAst]);
|
||||
|
||||
@ -147,22 +143,22 @@ function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): str
|
||||
|
||||
function _parseAnimationTransitionExpr(
|
||||
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
||||
var expressions: AnimationStateTransitionExpression[] = [];
|
||||
const expressions: AnimationStateTransitionExpression[] = [];
|
||||
if (eventStr[0] == ':') {
|
||||
eventStr = _parseAnimationAlias(eventStr, errors);
|
||||
}
|
||||
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
const match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
if (!isPresent(match) || match.length < 4) {
|
||||
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
||||
return expressions;
|
||||
}
|
||||
|
||||
var fromState = match[1];
|
||||
var separator = match[2];
|
||||
var toState = match[3];
|
||||
const fromState = match[1];
|
||||
const separator = match[2];
|
||||
const toState = match[3];
|
||||
expressions.push(new AnimationStateTransitionExpression(fromState, toState));
|
||||
|
||||
var isFullAnyStateExpr = fromState == ANY_STATE && toState == ANY_STATE;
|
||||
const isFullAnyStateExpr = fromState == ANY_STATE && toState == ANY_STATE;
|
||||
if (separator[0] == '<' && !isFullAnyStateExpr) {
|
||||
expressions.push(new AnimationStateTransitionExpression(toState, fromState));
|
||||
}
|
||||
@ -176,13 +172,31 @@ function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnima
|
||||
|
||||
function _normalizeStyleMetadata(
|
||||
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
||||
var normalizedStyles: {[key: string]: string | number}[] = [];
|
||||
schema: ElementSchemaRegistry, errors: AnimationParseError[],
|
||||
permitStateReferences: boolean): {[key: string]: string | number}[] {
|
||||
const normalizedStyles: {[key: string]: string | number}[] = [];
|
||||
entry.styles.forEach(styleEntry => {
|
||||
if (typeof styleEntry === 'string') {
|
||||
normalizedStyles.push(..._resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
||||
if (permitStateReferences) {
|
||||
normalizedStyles.push(..._resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
||||
} else {
|
||||
errors.push(new AnimationParseError(
|
||||
`State based animations cannot contain references to other states`));
|
||||
}
|
||||
} else {
|
||||
normalizedStyles.push(<{[key: string]: string | number}>styleEntry);
|
||||
const stylesObj = <Styles>styleEntry;
|
||||
const normalizedStylesObj: Styles = {};
|
||||
Object.keys(stylesObj).forEach(propName => {
|
||||
const normalizedProp = schema.normalizeAnimationStyleProperty(propName);
|
||||
const normalizedOutput =
|
||||
schema.normalizeAnimationStyleValue(normalizedProp, propName, stylesObj[propName]);
|
||||
const normalizationError = normalizedOutput['error'];
|
||||
if (normalizationError) {
|
||||
errors.push(new AnimationParseError(normalizationError));
|
||||
}
|
||||
normalizedStylesObj[normalizedProp] = normalizedOutput['value'];
|
||||
});
|
||||
normalizedStyles.push(normalizedStylesObj);
|
||||
}
|
||||
});
|
||||
return normalizedStyles;
|
||||
@ -190,8 +204,8 @@ function _normalizeStyleMetadata(
|
||||
|
||||
function _normalizeStyleSteps(
|
||||
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): CompileAnimationMetadata {
|
||||
var steps = _normalizeStyleStepEntry(entry, stateStyles, errors);
|
||||
schema: ElementSchemaRegistry, errors: AnimationParseError[]): CompileAnimationMetadata {
|
||||
const steps = _normalizeStyleStepEntry(entry, stateStyles, schema, errors);
|
||||
return (entry instanceof CompileAnimationGroupMetadata) ?
|
||||
new CompileAnimationGroupMetadata(steps) :
|
||||
new CompileAnimationSequenceMetadata(steps);
|
||||
@ -200,8 +214,8 @@ function _normalizeStyleSteps(
|
||||
function _mergeAnimationStyles(
|
||||
stylesList: any[], newItem: {[key: string]: string | number} | string) {
|
||||
if (typeof newItem === 'object' && newItem !== null && stylesList.length > 0) {
|
||||
var lastIndex = stylesList.length - 1;
|
||||
var lastItem = stylesList[lastIndex];
|
||||
const lastIndex = stylesList.length - 1;
|
||||
const lastItem = stylesList[lastIndex];
|
||||
if (typeof lastItem === 'object' && lastItem !== null) {
|
||||
stylesList[lastIndex] = StringMapWrapper.merge(
|
||||
<{[key: string]: string | number}>lastItem, <{[key: string]: string | number}>newItem);
|
||||
@ -213,16 +227,16 @@ function _mergeAnimationStyles(
|
||||
|
||||
function _normalizeStyleStepEntry(
|
||||
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
||||
var steps: CompileAnimationMetadata[];
|
||||
schema: ElementSchemaRegistry, errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
||||
let steps: CompileAnimationMetadata[];
|
||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||
steps = entry.steps;
|
||||
} else {
|
||||
return [entry];
|
||||
}
|
||||
|
||||
var newSteps: CompileAnimationMetadata[] = [];
|
||||
var combinedStyles: Styles[];
|
||||
const newSteps: CompileAnimationMetadata[] = [];
|
||||
let combinedStyles: Styles[];
|
||||
steps.forEach(step => {
|
||||
if (step instanceof CompileAnimationStyleMetadata) {
|
||||
// this occurs when a style step is followed by a previous style step
|
||||
@ -232,7 +246,8 @@ function _normalizeStyleStepEntry(
|
||||
if (!isPresent(combinedStyles)) {
|
||||
combinedStyles = [];
|
||||
}
|
||||
_normalizeStyleMetadata(<CompileAnimationStyleMetadata>step, stateStyles, errors)
|
||||
_normalizeStyleMetadata(
|
||||
<CompileAnimationStyleMetadata>step, stateStyles, schema, errors, true)
|
||||
.forEach(entry => { _mergeAnimationStyles(combinedStyles, entry); });
|
||||
} else {
|
||||
// it is important that we create a metadata entry of the combined styles
|
||||
@ -247,16 +262,17 @@ function _normalizeStyleStepEntry(
|
||||
if (step instanceof CompileAnimationAnimateMetadata) {
|
||||
// we do not recurse into CompileAnimationAnimateMetadata since
|
||||
// those style steps are not going to be squashed
|
||||
var animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
||||
const animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
||||
if (animateStyleValue instanceof CompileAnimationStyleMetadata) {
|
||||
animateStyleValue.styles =
|
||||
_normalizeStyleMetadata(animateStyleValue, stateStyles, errors);
|
||||
_normalizeStyleMetadata(animateStyleValue, stateStyles, schema, errors, true);
|
||||
} else if (animateStyleValue instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||
animateStyleValue.steps.forEach(
|
||||
step => { step.styles = _normalizeStyleMetadata(step, stateStyles, errors); });
|
||||
animateStyleValue.steps.forEach(step => {
|
||||
step.styles = _normalizeStyleMetadata(step, stateStyles, schema, errors, true);
|
||||
});
|
||||
}
|
||||
} else if (step instanceof CompileAnimationWithStepsMetadata) {
|
||||
let innerSteps = _normalizeStyleStepEntry(step, stateStyles, errors);
|
||||
const innerSteps = _normalizeStyleStepEntry(step, stateStyles, schema, errors);
|
||||
step = step instanceof CompileAnimationGroupMetadata ?
|
||||
new CompileAnimationGroupMetadata(innerSteps) :
|
||||
new CompileAnimationSequenceMetadata(innerSteps);
|
||||
@ -278,12 +294,12 @@ function _normalizeStyleStepEntry(
|
||||
function _resolveStylesFromState(
|
||||
stateName: string, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]) {
|
||||
var styles: Styles[] = [];
|
||||
const styles: Styles[] = [];
|
||||
if (stateName[0] != ':') {
|
||||
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
|
||||
} else {
|
||||
var normalizedStateName = stateName.substring(1);
|
||||
var value = stateStyles[normalizedStateName];
|
||||
const normalizedStateName = stateName.substring(1);
|
||||
const value = stateStyles[normalizedStateName];
|
||||
if (!isPresent(value)) {
|
||||
errors.push(new AnimationParseError(
|
||||
`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
|
||||
@ -306,8 +322,8 @@ function _parseAnimationKeyframes(
|
||||
keyframeSequence: CompileAnimationKeyframesSequenceMetadata, currentTime: number,
|
||||
collectedStyles: StylesCollection, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): AnimationKeyframeAst[] {
|
||||
var totalEntries = keyframeSequence.steps.length;
|
||||
var totalOffsets = 0;
|
||||
const totalEntries = keyframeSequence.steps.length;
|
||||
let totalOffsets = 0;
|
||||
keyframeSequence.steps.forEach(step => totalOffsets += (isPresent(step.offset) ? 1 : 0));
|
||||
|
||||
if (totalOffsets > 0 && totalOffsets < totalEntries) {
|
||||
@ -316,15 +332,15 @@ function _parseAnimationKeyframes(
|
||||
totalOffsets = totalEntries;
|
||||
}
|
||||
|
||||
var limit = totalEntries - 1;
|
||||
var margin = totalOffsets == 0 ? (1 / limit) : 0;
|
||||
var rawKeyframes: any[] /** TODO #9100 */ = [];
|
||||
var index = 0;
|
||||
var doSortKeyframes = false;
|
||||
var lastOffset = 0;
|
||||
let limit = totalEntries - 1;
|
||||
const margin = totalOffsets == 0 ? (1 / limit) : 0;
|
||||
const rawKeyframes: any[] /** TODO #9100 */ = [];
|
||||
let index = 0;
|
||||
let doSortKeyframes = false;
|
||||
let lastOffset = 0;
|
||||
keyframeSequence.steps.forEach(styleMetadata => {
|
||||
var offset = styleMetadata.offset;
|
||||
var keyframeStyles: Styles = {};
|
||||
let offset = styleMetadata.offset;
|
||||
const keyframeStyles: Styles = {};
|
||||
styleMetadata.styles.forEach(entry => {
|
||||
Object.keys(entry).forEach(prop => {
|
||||
if (prop != 'offset') {
|
||||
@ -348,23 +364,23 @@ function _parseAnimationKeyframes(
|
||||
rawKeyframes.sort((a, b) => a[0] <= b[0] ? -1 : 1);
|
||||
}
|
||||
|
||||
var firstKeyframe = rawKeyframes[0];
|
||||
let firstKeyframe = rawKeyframes[0];
|
||||
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
||||
rawKeyframes.splice(0, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
||||
}
|
||||
|
||||
var firstKeyframeStyles = firstKeyframe[1];
|
||||
const firstKeyframeStyles = firstKeyframe[1];
|
||||
limit = rawKeyframes.length - 1;
|
||||
var lastKeyframe = rawKeyframes[limit];
|
||||
let lastKeyframe = rawKeyframes[limit];
|
||||
if (lastKeyframe[0] != _TERMINAL_KEYFRAME) {
|
||||
rawKeyframes.push(lastKeyframe = [_TERMINAL_KEYFRAME, {}]);
|
||||
limit++;
|
||||
}
|
||||
|
||||
var lastKeyframeStyles = lastKeyframe[1];
|
||||
const lastKeyframeStyles = lastKeyframe[1];
|
||||
for (let i = 1; i <= limit; i++) {
|
||||
let entry = rawKeyframes[i];
|
||||
let styles = entry[1];
|
||||
const entry = rawKeyframes[i];
|
||||
const styles = entry[1];
|
||||
|
||||
Object.keys(styles).forEach(prop => {
|
||||
if (!isPresent(firstKeyframeStyles[prop])) {
|
||||
@ -374,8 +390,8 @@ function _parseAnimationKeyframes(
|
||||
}
|
||||
|
||||
for (let i = limit - 1; i >= 0; i--) {
|
||||
let entry = rawKeyframes[i];
|
||||
let styles = entry[1];
|
||||
const entry = rawKeyframes[i];
|
||||
const styles = entry[1];
|
||||
|
||||
Object.keys(styles).forEach(prop => {
|
||||
if (!isPresent(lastKeyframeStyles[prop])) {
|
||||
@ -391,21 +407,21 @@ function _parseAnimationKeyframes(
|
||||
function _parseTransitionAnimation(
|
||||
entry: CompileAnimationMetadata, currentTime: number, collectedStyles: StylesCollection,
|
||||
stateStyles: {[key: string]: AnimationStylesAst}, errors: AnimationParseError[]): AnimationAst {
|
||||
var ast: any /** TODO #9100 */;
|
||||
var playTime = 0;
|
||||
var startingTime = currentTime;
|
||||
let ast: any /** TODO #9100 */;
|
||||
let playTime = 0;
|
||||
const startingTime = currentTime;
|
||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||
var maxDuration = 0;
|
||||
var steps: any[] /** TODO #9100 */ = [];
|
||||
var isGroup = entry instanceof CompileAnimationGroupMetadata;
|
||||
var previousStyles: any /** TODO #9100 */;
|
||||
let maxDuration = 0;
|
||||
const steps: any[] /** TODO #9100 */ = [];
|
||||
const isGroup = entry instanceof CompileAnimationGroupMetadata;
|
||||
let previousStyles: any /** TODO #9100 */;
|
||||
entry.steps.forEach(entry => {
|
||||
// these will get picked up by the next step...
|
||||
var time = isGroup ? startingTime : currentTime;
|
||||
const time = isGroup ? startingTime : currentTime;
|
||||
if (entry instanceof CompileAnimationStyleMetadata) {
|
||||
entry.styles.forEach(stylesEntry => {
|
||||
// by this point we know that we only have stringmap values
|
||||
var map = stylesEntry as Styles;
|
||||
const map = stylesEntry as Styles;
|
||||
Object.keys(map).forEach(
|
||||
prop => { collectedStyles.insertAtTime(prop, time, map[prop]); });
|
||||
});
|
||||
@ -413,26 +429,26 @@ function _parseTransitionAnimation(
|
||||
return;
|
||||
}
|
||||
|
||||
var innerAst = _parseTransitionAnimation(entry, time, collectedStyles, stateStyles, errors);
|
||||
const innerAst = _parseTransitionAnimation(entry, time, collectedStyles, stateStyles, errors);
|
||||
if (isPresent(previousStyles)) {
|
||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||
let startingStyles = new AnimationStylesAst(previousStyles);
|
||||
const startingStyles = new AnimationStylesAst(previousStyles);
|
||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||
} else {
|
||||
var innerStep = <AnimationStepAst>innerAst;
|
||||
const innerStep = <AnimationStepAst>innerAst;
|
||||
innerStep.startingStyles.styles.push(...previousStyles);
|
||||
}
|
||||
previousStyles = null;
|
||||
}
|
||||
|
||||
var astDuration = innerAst.playTime;
|
||||
const astDuration = innerAst.playTime;
|
||||
currentTime += astDuration;
|
||||
playTime += astDuration;
|
||||
maxDuration = Math.max(astDuration, maxDuration);
|
||||
steps.push(innerAst);
|
||||
});
|
||||
if (isPresent(previousStyles)) {
|
||||
let startingStyles = new AnimationStylesAst(previousStyles);
|
||||
const startingStyles = new AnimationStylesAst(previousStyles);
|
||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||
}
|
||||
if (isGroup) {
|
||||
@ -443,18 +459,18 @@ function _parseTransitionAnimation(
|
||||
ast = new AnimationSequenceAst(steps);
|
||||
}
|
||||
} else if (entry instanceof CompileAnimationAnimateMetadata) {
|
||||
var timings = _parseTimeExpression(entry.timings, errors);
|
||||
var styles = entry.styles;
|
||||
const timings = _parseTimeExpression(entry.timings, errors);
|
||||
const styles = entry.styles;
|
||||
|
||||
var keyframes: any /** TODO #9100 */;
|
||||
let keyframes: any /** TODO #9100 */;
|
||||
if (styles instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||
keyframes =
|
||||
_parseAnimationKeyframes(styles, currentTime, collectedStyles, stateStyles, errors);
|
||||
} else {
|
||||
let styleData = <CompileAnimationStyleMetadata>styles;
|
||||
let offset = _TERMINAL_KEYFRAME;
|
||||
let styleAst = new AnimationStylesAst(styleData.styles as Styles[]);
|
||||
var keyframe = new AnimationKeyframeAst(offset, styleAst);
|
||||
const styleData = <CompileAnimationStyleMetadata>styles;
|
||||
const offset = _TERMINAL_KEYFRAME;
|
||||
const styleAst = new AnimationStylesAst(styleData.styles as Styles[]);
|
||||
const keyframe = new AnimationKeyframeAst(offset, styleAst);
|
||||
keyframes = [keyframe];
|
||||
}
|
||||
|
||||
@ -483,10 +499,10 @@ function _fillAnimationAstStartingKeyframes(
|
||||
ast: AnimationAst, collectedStyles: StylesCollection, errors: AnimationParseError[]): void {
|
||||
// steps that only contain style will not be filled
|
||||
if ((ast instanceof AnimationStepAst) && ast.keyframes.length > 0) {
|
||||
var keyframes = ast.keyframes;
|
||||
const keyframes = ast.keyframes;
|
||||
if (keyframes.length == 1) {
|
||||
var endKeyframe = keyframes[0];
|
||||
var startKeyframe = _createStartKeyframeFromEndKeyframe(
|
||||
const endKeyframe = keyframes[0];
|
||||
const startKeyframe = _createStartKeyframeFromEndKeyframe(
|
||||
endKeyframe, ast.startTime, ast.playTime, collectedStyles, errors);
|
||||
ast.keyframes = [startKeyframe, endKeyframe];
|
||||
}
|
||||
@ -497,10 +513,10 @@ function _fillAnimationAstStartingKeyframes(
|
||||
|
||||
function _parseTimeExpression(
|
||||
exp: string | number, errors: AnimationParseError[]): _AnimationTimings {
|
||||
var regex = /^([\.\d]+)(m?s)(?:\s+([\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?/i;
|
||||
var duration: number;
|
||||
var delay: number = 0;
|
||||
var easing: string = null;
|
||||
const regex = /^([\.\d]+)(m?s)(?:\s+([\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?/i;
|
||||
let duration: number;
|
||||
let delay: number = 0;
|
||||
let easing: string = null;
|
||||
if (typeof exp === 'string') {
|
||||
const matches = exp.match(regex);
|
||||
if (matches === null) {
|
||||
@ -508,24 +524,24 @@ function _parseTimeExpression(
|
||||
return new _AnimationTimings(0, 0, null);
|
||||
}
|
||||
|
||||
var durationMatch = parseFloat(matches[1]);
|
||||
var durationUnit = matches[2];
|
||||
let durationMatch = parseFloat(matches[1]);
|
||||
const durationUnit = matches[2];
|
||||
if (durationUnit == 's') {
|
||||
durationMatch *= _ONE_SECOND;
|
||||
}
|
||||
duration = Math.floor(durationMatch);
|
||||
|
||||
var delayMatch = matches[3];
|
||||
var delayUnit = matches[4];
|
||||
const delayMatch = matches[3];
|
||||
const delayUnit = matches[4];
|
||||
if (isPresent(delayMatch)) {
|
||||
var delayVal: number = parseFloat(delayMatch);
|
||||
let delayVal: number = parseFloat(delayMatch);
|
||||
if (isPresent(delayUnit) && delayUnit == 's') {
|
||||
delayVal *= _ONE_SECOND;
|
||||
}
|
||||
delay = Math.floor(delayVal);
|
||||
}
|
||||
|
||||
var easingVal = matches[5];
|
||||
const easingVal = matches[5];
|
||||
if (!isBlank(easingVal)) {
|
||||
easing = easingVal;
|
||||
}
|
||||
@ -539,15 +555,15 @@ function _parseTimeExpression(
|
||||
function _createStartKeyframeFromEndKeyframe(
|
||||
endKeyframe: AnimationKeyframeAst, startTime: number, duration: number,
|
||||
collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst {
|
||||
var values: Styles = {};
|
||||
var endTime = startTime + duration;
|
||||
const values: Styles = {};
|
||||
const endTime = startTime + duration;
|
||||
endKeyframe.styles.styles.forEach((styleData: Styles) => {
|
||||
Object.keys(styleData).forEach(prop => {
|
||||
const val = styleData[prop];
|
||||
if (prop == 'offset') return;
|
||||
|
||||
var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);
|
||||
var resultEntry: any /** TODO #9100 */, nextEntry: any /** TODO #9100 */,
|
||||
const resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);
|
||||
let resultEntry: any /** TODO #9100 */, nextEntry: any /** TODO #9100 */,
|
||||
value: any /** TODO #9100 */;
|
||||
if (isPresent(resultIndex)) {
|
||||
resultEntry = collectedStyles.getByIndex(prop, resultIndex);
|
||||
|
@ -20,16 +20,16 @@ export class StylesCollection {
|
||||
styles: {[key: string]: StylesCollectionEntry[]} = {};
|
||||
|
||||
insertAtTime(property: string, time: number, value: string|number) {
|
||||
var tuple = new StylesCollectionEntry(time, value);
|
||||
var entries = this.styles[property];
|
||||
const tuple = new StylesCollectionEntry(time, value);
|
||||
let entries = this.styles[property];
|
||||
if (!isPresent(entries)) {
|
||||
entries = this.styles[property] = [];
|
||||
}
|
||||
|
||||
// insert this at the right stop in the array
|
||||
// this way we can keep it sorted
|
||||
var insertionIndex = 0;
|
||||
for (var i = entries.length - 1; i >= 0; i--) {
|
||||
let insertionIndex = 0;
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
if (entries[i].time <= time) {
|
||||
insertionIndex = i + 1;
|
||||
break;
|
||||
@ -40,7 +40,7 @@ export class StylesCollection {
|
||||
}
|
||||
|
||||
getByIndex(property: string, index: number): StylesCollectionEntry {
|
||||
var items = this.styles[property];
|
||||
const items = this.styles[property];
|
||||
if (isPresent(items)) {
|
||||
return index >= items.length ? null : items[index];
|
||||
}
|
||||
@ -48,9 +48,9 @@ export class StylesCollection {
|
||||
}
|
||||
|
||||
indexOfAtOrBeforeTime(property: string, time: number): number {
|
||||
var entries = this.styles[property];
|
||||
const entries = this.styles[property];
|
||||
if (isPresent(entries)) {
|
||||
for (var i = entries.length - 1; i >= 0; i--) {
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
if (entries[i].time <= time) return i;
|
||||
}
|
||||
}
|
||||
|
@ -8,139 +8,58 @@
|
||||
|
||||
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, MapWrapper} 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, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
import {AnimationParser} from '../animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, 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 {AotCompilerOptions} from './compiler_options';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
|
||||
export class SourceModule {
|
||||
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
|
||||
}
|
||||
|
||||
// Returns all the source files and a mapping from modules to directives
|
||||
export function analyzeNgModules(
|
||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||
metadataResolver: CompileMetadataResolver): {
|
||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
|
||||
} {
|
||||
const {
|
||||
ngModules: programNgModules,
|
||||
pipesAndDirectives: programPipesOrDirectives,
|
||||
} = _extractModulesAndPipesOrDirectives(programStaticSymbols, metadataResolver);
|
||||
|
||||
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||
|
||||
programNgModules.forEach(modMeta => {
|
||||
if (options.transitiveModules) {
|
||||
// For every input modules add the list of transitively included modules
|
||||
modMeta.transitiveModule.modules.forEach(
|
||||
modMeta => { moduleMetasByRef.set(modMeta.type.reference, modMeta); });
|
||||
} else {
|
||||
moduleMetasByRef.set(modMeta.type.reference, modMeta);
|
||||
}
|
||||
});
|
||||
|
||||
const ngModuleMetas = MapWrapper.values(moduleMetasByRef);
|
||||
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((dirMeta: CompileDirectiveMetadata) => {
|
||||
const fileUrl = dirMeta.type.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngDirectivesByFile.set(
|
||||
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirMeta.type.reference));
|
||||
ngModuleByPipeOrDirective.set(dirMeta.type.reference, ngModuleMeta);
|
||||
});
|
||||
|
||||
ngModuleMeta.declaredPipes.forEach((pipeMeta: CompilePipeMetadata) => {
|
||||
const fileUrl = pipeMeta.type.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngModuleByPipeOrDirective.set(pipeMeta.type.reference, ngModuleMeta);
|
||||
});
|
||||
});
|
||||
|
||||
// Throw an error if any of the program pipe or directives is not declared by a module
|
||||
const symbolsMissingModule =
|
||||
programPipesOrDirectives.filter(s => !ngModuleByPipeOrDirective.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'));
|
||||
}
|
||||
|
||||
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 {
|
||||
private _animationParser = new AnimationParser();
|
||||
export class AotCompiler {
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
private _metadataResolver: CompileMetadataResolver,
|
||||
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
|
||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||
private _localeId: string, private _translationFormat: string) {}
|
||||
private _localeId: string, private _translationFormat: string,
|
||||
private _animationParser: AnimationParser, private _staticReflector: StaticReflector,
|
||||
private _options: AotCompilerOptions) {}
|
||||
|
||||
clearCache() {
|
||||
this._directiveNormalizer.clearCache();
|
||||
this._metadataResolver.clearCache();
|
||||
}
|
||||
clearCache() { this._metadataResolver.clearCache(); }
|
||||
|
||||
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
|
||||
Promise<SourceModule[]> {
|
||||
const {ngModuleByPipeOrDirective, files} =
|
||||
analyzeNgModules(staticSymbols, options, this._metadataResolver);
|
||||
|
||||
const sourceModules = files.map(
|
||||
file => this._compileSrcFile(
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
||||
|
||||
return Promise.all(sourceModules)
|
||||
.then((modules: SourceModule[][]) => ListWrapper.flatten(modules));
|
||||
compileAll(rootFiles: string[]): Promise<SourceModule[]> {
|
||||
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
||||
return loadNgModuleDirectives(ngModules).then(() => {
|
||||
const sourceModules = files.map(
|
||||
file => this._compileSrcFile(
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
||||
return ListWrapper.flatten(sourceModules);
|
||||
});
|
||||
}
|
||||
|
||||
private _compileSrcFile(
|
||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
directives: StaticSymbol[], ngModules: StaticSymbol[]): Promise<SourceModule[]> {
|
||||
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
|
||||
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||
const statements: o.Statement[] = [];
|
||||
const exportedVars: string[] = [];
|
||||
@ -155,48 +74,38 @@ export class OfflineCompiler {
|
||||
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
|
||||
|
||||
// compile components
|
||||
return Promise
|
||||
.all(directives.map((dirType) => {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||
if (!compMeta.isComponent) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||
if (!ngModule) {
|
||||
throw new Error(
|
||||
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
||||
}
|
||||
directives.forEach((dirType) => {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||
if (!compMeta.isComponent) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||
if (!ngModule) {
|
||||
throw new Error(
|
||||
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
||||
}
|
||||
|
||||
return Promise
|
||||
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
||||
dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
||||
.then((normalizedCompWithDirectives) => {
|
||||
const [compMeta, ...dirMetas] = normalizedCompWithDirectives;
|
||||
_assertComponent(compMeta);
|
||||
_assertComponent(compMeta);
|
||||
|
||||
// compile styles
|
||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||
outputSourceModules.push(
|
||||
this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||
});
|
||||
// compile styles
|
||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||
});
|
||||
|
||||
// compile components
|
||||
exportedVars.push(
|
||||
this._compileComponentFactory(compMeta, fileSuffix, statements),
|
||||
this._compileComponent(
|
||||
compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas,
|
||||
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
||||
});
|
||||
}))
|
||||
.then(() => {
|
||||
if (statements.length > 0) {
|
||||
const srcModule = this._codegenSourceModule(
|
||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||
outputSourceModules.unshift(srcModule);
|
||||
}
|
||||
return outputSourceModules;
|
||||
});
|
||||
// compile components
|
||||
exportedVars.push(
|
||||
this._compileComponentFactory(compMeta, ngModule, fileSuffix, statements),
|
||||
this._compileComponent(
|
||||
compMeta, ngModule, ngModule.transitiveModule.directives,
|
||||
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
||||
});
|
||||
if (statements.length > 0) {
|
||||
const srcModule = this._codegenSourceModule(
|
||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||
outputSourceModules.unshift(srcModule);
|
||||
}
|
||||
return outputSourceModules;
|
||||
}
|
||||
|
||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||
@ -238,11 +147,11 @@ export class OfflineCompiler {
|
||||
}
|
||||
|
||||
private _compileComponentFactory(
|
||||
compMeta: CompileDirectiveMetadata, fileSuffix: string,
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
||||
targetStatements: o.Statement[]): string {
|
||||
const hostMeta = createHostComponentMeta(compMeta);
|
||||
const hostViewFactoryVar =
|
||||
this._compileComponent(hostMeta, [compMeta], [], [], null, fileSuffix, targetStatements);
|
||||
const hostViewFactoryVar = this._compileComponent(
|
||||
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||
targetStatements.push(
|
||||
o.variable(compFactoryVar)
|
||||
@ -262,12 +171,18 @@ export class OfflineCompiler {
|
||||
}
|
||||
|
||||
private _compileComponent(
|
||||
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
|
||||
directiveIdentifiers: CompileIdentifierMetadata[], componentStyles: CompiledStylesheet,
|
||||
fileSuffix: string, targetStatements: o.Statement[]): string {
|
||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||
const directives =
|
||||
directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
|
||||
const pipes = ngModule.transitiveModule.pipes.map(
|
||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name);
|
||||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||
compMeta.type.name);
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
@ -276,10 +191,9 @@ export class OfflineCompiler {
|
||||
if (componentStyles) {
|
||||
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
||||
}
|
||||
compiledAnimations.forEach(
|
||||
entry => { entry.statements.forEach(statement => { targetStatements.push(statement); }); });
|
||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||
targetStatements.push(..._resolveViewStatements(viewResult));
|
||||
return viewResult.viewFactoryVar;
|
||||
return viewResult.viewClassVar;
|
||||
}
|
||||
|
||||
private _codgenStyles(
|
||||
@ -302,8 +216,8 @@ export class OfflineCompiler {
|
||||
|
||||
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
if (dep instanceof ViewFactoryDependency) {
|
||||
const vfd = <ViewFactoryDependency>dep;
|
||||
if (dep instanceof ViewClassDependency) {
|
||||
const vfd = <ViewClassDependency>dep;
|
||||
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
|
||||
} else if (dep instanceof ComponentFactoryDependency) {
|
||||
const cfd = <ComponentFactoryDependency>dep;
|
||||
@ -359,27 +273,172 @@ function _splitTypescriptSuffix(path: string): string[] {
|
||||
return [path, ''];
|
||||
}
|
||||
|
||||
// Group the symbols by types:
|
||||
// - NgModules,
|
||||
// - Pipes and Directives.
|
||||
function _extractModulesAndPipesOrDirectives(
|
||||
programStaticSymbols: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
|
||||
const ngModules: CompileNgModuleMetadata[] = [];
|
||||
const pipesAndDirectives: StaticSymbol[] = [];
|
||||
export interface NgAnalyzedModules {
|
||||
ngModules: CompileNgModuleMetadata[];
|
||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>;
|
||||
symbolsMissingModule?: StaticSymbol[];
|
||||
}
|
||||
|
||||
programStaticSymbols.forEach(staticSymbol => {
|
||||
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
||||
const directive = metadataResolver.getDirectiveMetadata(staticSymbol, false);
|
||||
const pipe = metadataResolver.getPipeMetadata(<any>staticSymbol, false);
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (ngModule) {
|
||||
ngModules.push(ngModule);
|
||||
} else if (directive) {
|
||||
pipesAndDirectives.push(staticSymbol);
|
||||
} else if (pipe) {
|
||||
pipesAndDirectives.push(staticSymbol);
|
||||
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;
|
||||
}
|
||||
|
||||
// Wait for the directives in the given modules have been loaded
|
||||
export function loadNgModuleDirectives(ngModules: CompileNgModuleMetadata[]) {
|
||||
return Promise
|
||||
.all(ListWrapper.flatten(ngModules.map(
|
||||
(ngModule) => ngModule.transitiveModule.directiveLoaders.map(loader => loader()))))
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
function _analyzeNgModules(
|
||||
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 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,
|
||||
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.log(`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 {ngModules, pipesAndDirectives};
|
||||
return staticSymbols;
|
||||
}
|
||||
|
||||
// Load the NgModules and check
|
||||
// that all directives / pipes that are present in the program
|
||||
// are also declared by a module.
|
||||
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 addNgModule = (staticSymbol: any) => {
|
||||
if (ngModules.has(staticSymbol) || !_filterFileByPatterns(staticSymbol.filePath, options)) {
|
||||
return false;
|
||||
}
|
||||
const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false);
|
||||
if (ngModule) {
|
||||
ngModules.set(ngModule.type.reference, ngModule);
|
||||
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
|
||||
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
|
||||
// For every input module add the list of transitively included modules
|
||||
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
|
||||
}
|
||||
return !!ngModule;
|
||||
};
|
||||
programStaticSymbols.forEach((staticSymbol) => {
|
||||
if (!addNgModule(staticSymbol) &&
|
||||
(metadataResolver.isDirective(staticSymbol) || metadataResolver.isPipe(staticSymbol))) {
|
||||
programPipesAndDirectives.push(staticSymbol);
|
||||
}
|
||||
});
|
||||
|
||||
// Throw an error if any of the program pipe or directives is not declared by a module
|
||||
const symbolsMissingModule =
|
||||
programPipesAndDirectives.filter(s => !ngModulePipesAndDirective.has(s));
|
||||
|
||||
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
||||
}
|
||||
|
||||
function _filterFileByPatterns(
|
||||
fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
|
||||
let match = true;
|
||||
if (options.includeFilePattern) {
|
||||
match = match && !!options.includeFilePattern.exec(fileName);
|
||||
}
|
||||
if (options.excludeFilePattern) {
|
||||
match = match && !options.excludeFilePattern.exec(fileName);
|
||||
}
|
||||
return match;
|
||||
}
|
75
modules/@angular/compiler/src/aot/compiler_factory.ts
Normal file
75
modules/@angular/compiler/src/aot/compiler_factory.ts
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @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';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 resolver = new CompileMetadataResolver(
|
||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||
new PipeResolver(staticReflector), 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), options.locale,
|
||||
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
||||
return {compiler, reflector: staticReflector};
|
||||
}
|
24
modules/@angular/compiler/src/aot/compiler_host.ts
Normal file
24
modules/@angular/compiler/src/aot/compiler_host.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ImportResolver} from '../output/path_util';
|
||||
|
||||
import {StaticReflectorHost} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
|
||||
|
||||
/**
|
||||
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
|
||||
* services and from underlying file systems.
|
||||
*/
|
||||
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver {
|
||||
/**
|
||||
* Loads a resource (e.g. html / css)
|
||||
*/
|
||||
loadResource(path: string): Promise<string>;
|
||||
}
|
@ -6,9 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var _FileReader = FileReader;
|
||||
export {_FileReader as FileReader};
|
||||
|
||||
export class Uint8ArrayWrapper {
|
||||
static create(buffer: ArrayBuffer) { return new Uint8Array(buffer); }
|
||||
export interface AotCompilerOptions {
|
||||
debug?: boolean;
|
||||
locale?: string;
|
||||
i18nFormat?: string;
|
||||
translations?: string;
|
||||
includeFilePattern?: RegExp;
|
||||
excludeFilePattern?: RegExp;
|
||||
}
|
@ -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,6 +68,7 @@ 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 parameterCache = new Map<StaticSymbol, any[]>();
|
||||
@ -69,26 +76,30 @@ export class StaticReflector implements ReflectorReader {
|
||||
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()) {
|
||||
this.initializeConversionMap();
|
||||
}
|
||||
|
||||
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) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
if (classMetadata['decorators']) {
|
||||
annotations = this.simplify(type, classMetadata['decorators']);
|
||||
} else {
|
||||
@ -102,11 +113,11 @@ export class StaticReflector implements ReflectorReader {
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!propMetadata) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : {};
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : {};
|
||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||
let prop = (<any[]>propData)
|
||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||
const prop = (<any[]>propData)
|
||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||
if (prop && prop['decorators']) {
|
||||
return this.simplify(type, prop['decorators']);
|
||||
} else {
|
||||
@ -125,21 +136,20 @@ export class StaticReflector implements ReflectorReader {
|
||||
try {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
if (!parameters) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : null;
|
||||
let ctorData = members ? members['__ctor__'] : null;
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : null;
|
||||
const ctorData = members ? members['__ctor__'] : null;
|
||||
if (ctorData) {
|
||||
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||
let parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
||||
let parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
||||
|
||||
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) => {
|
||||
let nestedResult: any[] = [];
|
||||
const nestedResult: any[] = [];
|
||||
if (paramType) {
|
||||
nestedResult.push(paramType);
|
||||
}
|
||||
let decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||
const decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||
if (decorators) {
|
||||
nestedResult.push(...decorators);
|
||||
}
|
||||
@ -180,59 +190,145 @@ export class StaticReflector implements ReflectorReader {
|
||||
|
||||
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.findDeclaration(diDecorators, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Self'), Self);
|
||||
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.host.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||
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.host.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Input'), Input);
|
||||
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Output'), Output);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||
this.findDeclaration(coreDecorators, 'Directive'), Directive);
|
||||
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.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 resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
||||
const resolveModule = (moduleName: string): string => {
|
||||
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
||||
if (!resolvedModulePath) {
|
||||
throw new Error(`Could not resolve module '${moduleName}' relative to file ${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;
|
||||
}
|
||||
staticSymbol = this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!staticSymbol) {
|
||||
// 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) {
|
||||
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 */
|
||||
@ -245,10 +341,10 @@ 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 =
|
||||
_this.findDeclaration(expression['module'], expression['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']);
|
||||
staticSymbol = _this.getStaticSymbol(context.filePath, expression['name']);
|
||||
}
|
||||
return staticSymbol;
|
||||
}
|
||||
@ -273,7 +369,7 @@ export class StaticReflector implements ReflectorReader {
|
||||
function simplifyCall(expression: any) {
|
||||
let callContext: {[name: string]: string}|undefined = undefined;
|
||||
if (expression['__symbolic'] == 'call') {
|
||||
let target = expression['expression'];
|
||||
const target = expression['expression'];
|
||||
let functionSymbol: StaticSymbol;
|
||||
let targetFunction: any;
|
||||
if (target) {
|
||||
@ -316,7 +412,7 @@ export class StaticReflector implements ReflectorReader {
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
functionScope.define(parameters[i], args[i]);
|
||||
}
|
||||
let oldScope = scope;
|
||||
const oldScope = scope;
|
||||
let result: any;
|
||||
try {
|
||||
scope = functionScope.done();
|
||||
@ -347,19 +443,19 @@ export class StaticReflector implements ReflectorReader {
|
||||
return expression;
|
||||
}
|
||||
if (expression instanceof Array) {
|
||||
let result: any[] = [];
|
||||
for (let item of (<any>expression)) {
|
||||
const result: any[] = [];
|
||||
for (const item of (<any>expression)) {
|
||||
// Check for a spread expression
|
||||
if (item && item.__symbolic === 'spread') {
|
||||
let spreadArray = simplify(item.expression);
|
||||
const spreadArray = simplify(item.expression);
|
||||
if (Array.isArray(spreadArray)) {
|
||||
for (let spreadItem of spreadArray) {
|
||||
for (const spreadItem of spreadArray) {
|
||||
result.push(spreadItem);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let value = simplify(item);
|
||||
const value = simplify(item);
|
||||
if (shouldIgnore(value)) {
|
||||
continue;
|
||||
}
|
||||
@ -457,8 +553,7 @@ export class StaticReflector implements ReflectorReader {
|
||||
const members = selectTarget.members ?
|
||||
(selectTarget.members as string[]).concat(member) :
|
||||
[member];
|
||||
return _this.host.getStaticSymbol(
|
||||
selectTarget.filePath, selectTarget.name, members);
|
||||
return _this.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
||||
}
|
||||
}
|
||||
const member = simplify(expression['member']);
|
||||
@ -466,8 +561,8 @@ export class StaticReflector implements ReflectorReader {
|
||||
return null;
|
||||
case 'reference':
|
||||
if (!expression.module) {
|
||||
let name: string = expression['name'];
|
||||
let localValue = scope.resolve(name);
|
||||
const name: string = expression['name'];
|
||||
const localValue = scope.resolve(name);
|
||||
if (localValue != BindingScope.missing) {
|
||||
return localValue;
|
||||
}
|
||||
@ -493,10 +588,10 @@ 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 =
|
||||
_this.findDeclaration(target['module'], target['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, target['name']);
|
||||
staticSymbol = _this.getStaticSymbol(context.filePath, target['name']);
|
||||
}
|
||||
let converter = _this.conversionMap.get(staticSymbol);
|
||||
if (converter) {
|
||||
@ -516,6 +611,8 @@ export class StaticReflector implements ReflectorReader {
|
||||
if (expression['line']) {
|
||||
message =
|
||||
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
|
||||
throw positionalError(
|
||||
message, context.filePath, expression['line'], expression['character']);
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
@ -529,7 +626,11 @@ export class StaticReflector implements ReflectorReader {
|
||||
try {
|
||||
return simplify(value);
|
||||
} catch (e) {
|
||||
throw new Error(`${e.message}, resolving symbol ${context.name} in ${context.filePath}`);
|
||||
const message = `${e.message}, resolving symbol ${context.name} in ${context.filePath}`;
|
||||
if (e.fileName) {
|
||||
throw positionalError(message, e.fileName, e.line, e.column);
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,10 +647,15 @@ 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 =
|
||||
@ -595,6 +701,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;
|
||||
}
|
||||
@ -608,7 +715,7 @@ function mapStringMap(input: {[key: string]: any}, transform: (value: any, key:
|
||||
if (!input) return {};
|
||||
const result: {[key: string]: any} = {};
|
||||
Object.keys(input).forEach((key) => {
|
||||
let value = transform(input[key], key);
|
||||
const value = transform(input[key], key);
|
||||
if (!shouldIgnore(value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
@ -659,3 +766,11 @@ function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
||||
function shouldIgnore(value: any): boolean {
|
||||
return value && value.__symbolic == 'ignore';
|
||||
}
|
||||
|
||||
function positionalError(message: string, fileName: string, line: number, column: number): Error {
|
||||
const result = new Error(message);
|
||||
(result as any).fileName = fileName;
|
||||
(result as any).line = line;
|
||||
(result as any).column = column;
|
||||
return result;
|
||||
}
|
20
modules/@angular/compiler/src/aot/static_symbol.ts
Normal file
20
modules/@angular/compiler/src/aot/static_symbol.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
|
||||
*/
|
||||
|
||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
||||
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
|
||||
}
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* 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[]) {}
|
||||
}
|
@ -17,7 +17,7 @@ export function assertArrayOfStrings(identifier: string, value: any) {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
||||
}
|
||||
for (var i = 0; i < value.length; i += 1) {
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
if (typeof value[i] !== 'string') {
|
||||
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {ListWrapper, MapWrapper} from './facade/collection';
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {LifecycleHooks} from './private_import_core';
|
||||
import {CssSelector} from './selector';
|
||||
@ -98,6 +98,15 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier
|
||||
get identifier(): CompileIdentifierMetadata { return this; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A CompileSummary is the data needed to use a directive / pipe / module
|
||||
* 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 class CompileDiDependencyMetadata {
|
||||
isAttribute: boolean;
|
||||
isSelf: boolean;
|
||||
@ -105,33 +114,27 @@ export class CompileDiDependencyMetadata {
|
||||
isSkipSelf: boolean;
|
||||
isOptional: boolean;
|
||||
isValue: boolean;
|
||||
query: CompileQueryMetadata;
|
||||
viewQuery: CompileQueryMetadata;
|
||||
token: CompileTokenMetadata;
|
||||
value: any;
|
||||
|
||||
constructor(
|
||||
{isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, query, viewQuery, token,
|
||||
value}: {
|
||||
isAttribute?: boolean,
|
||||
isSelf?: boolean,
|
||||
isHost?: boolean,
|
||||
isSkipSelf?: boolean,
|
||||
isOptional?: boolean,
|
||||
isValue?: boolean,
|
||||
query?: CompileQueryMetadata,
|
||||
viewQuery?: CompileQueryMetadata,
|
||||
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.query = query;
|
||||
this.viewQuery = viewQuery;
|
||||
this.token = token;
|
||||
this.value = value;
|
||||
}
|
||||
@ -270,6 +273,16 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a template.
|
||||
*/
|
||||
@ -309,6 +322,34 @@ export class CompileTemplateMetadata {
|
||||
}
|
||||
this.interpolation = interpolation;
|
||||
}
|
||||
|
||||
toSummary(): CompileTemplateSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
animations: this.animations.map(anim => anim.name),
|
||||
ngContentSelectors: this.ngContentSelectors,
|
||||
encapsulation: this.encapsulation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface CompileDirectiveSummary extends CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
type: CompileTypeMetadata;
|
||||
isComponent: boolean;
|
||||
selector: string;
|
||||
exportAs: string;
|
||||
inputs: {[key: string]: string};
|
||||
outputs: {[key: string]: string};
|
||||
hostListeners: {[key: string]: string};
|
||||
hostProperties: {[key: string]: string};
|
||||
hostAttributes: {[key: string]: string};
|
||||
providers: CompileProviderMetadata[];
|
||||
viewProviders: CompileProviderMetadata[];
|
||||
queries: CompileQueryMetadata[];
|
||||
entryComponents: CompileIdentifierMetadata[];
|
||||
changeDetection: ChangeDetectionStrategy;
|
||||
template: CompileTemplateSummary;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,14 +373,12 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
entryComponents?: CompileTypeMetadata[],
|
||||
viewDirectives?: CompileTypeMetadata[],
|
||||
viewPipes?: CompileTypeMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
template?: CompileTemplateMetadata
|
||||
} = {}): CompileDirectiveMetadata {
|
||||
var hostListeners: {[key: string]: string} = {};
|
||||
var hostProperties: {[key: string]: string} = {};
|
||||
var hostAttributes: {[key: string]: string} = {};
|
||||
const hostListeners: {[key: string]: string} = {};
|
||||
const hostProperties: {[key: string]: string} = {};
|
||||
const hostAttributes: {[key: string]: string} = {};
|
||||
if (isPresent(host)) {
|
||||
Object.keys(host).forEach(key => {
|
||||
const value = host[key];
|
||||
@ -353,21 +392,21 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
}
|
||||
});
|
||||
}
|
||||
var inputsMap: {[key: string]: string} = {};
|
||||
const inputsMap: {[key: string]: string} = {};
|
||||
if (isPresent(inputs)) {
|
||||
inputs.forEach((bindConfig: string) => {
|
||||
// canonical syntax: `dirProp: elProp`
|
||||
// if there is no `:`, use dirProp = elProp
|
||||
var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||
const parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||
inputsMap[parts[0]] = parts[1];
|
||||
});
|
||||
}
|
||||
var outputsMap: {[key: string]: string} = {};
|
||||
const outputsMap: {[key: string]: string} = {};
|
||||
if (isPresent(outputs)) {
|
||||
outputs.forEach((bindConfig: string) => {
|
||||
// canonical syntax: `dirProp: elProp`
|
||||
// if there is no `:`, use dirProp = elProp
|
||||
var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||
const parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||
outputsMap[parts[0]] = parts[1];
|
||||
});
|
||||
}
|
||||
@ -402,8 +441,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
viewProviders: CompileProviderMetadata[];
|
||||
queries: CompileQueryMetadata[];
|
||||
viewQueries: CompileQueryMetadata[];
|
||||
// Note: Need to keep types here to prevent cycles!
|
||||
entryComponents: CompileTypeMetadata[];
|
||||
entryComponents: CompileIdentifierMetadata[];
|
||||
|
||||
template: CompileTemplateMetadata;
|
||||
|
||||
@ -427,9 +465,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
entryComponents?: CompileTypeMetadata[],
|
||||
viewDirectives?: CompileTypeMetadata[],
|
||||
viewPipes?: CompileTypeMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
template?: CompileTemplateMetadata,
|
||||
} = {}) {
|
||||
this.type = type;
|
||||
@ -452,6 +488,27 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompileDirectiveSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
type: this.type,
|
||||
isComponent: this.isComponent,
|
||||
selector: this.selector,
|
||||
exportAs: this.exportAs,
|
||||
inputs: this.inputs,
|
||||
outputs: this.outputs,
|
||||
hostListeners: this.hostListeners,
|
||||
hostProperties: this.hostProperties,
|
||||
hostAttributes: this.hostAttributes,
|
||||
providers: this.providers,
|
||||
viewProviders: this.viewProviders,
|
||||
queries: this.queries,
|
||||
entryComponents: this.entryComponents,
|
||||
changeDetection: this.changeDetection,
|
||||
template: this.template && this.template.toSummary()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -459,7 +516,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
*/
|
||||
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
||||
CompileDirectiveMetadata {
|
||||
var template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||
return CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({
|
||||
reference: Object,
|
||||
@ -489,6 +546,12 @@ export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
||||
});
|
||||
}
|
||||
|
||||
export interface CompilePipeSummary extends CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
type: CompileTypeMetadata;
|
||||
name: string;
|
||||
pure: boolean;
|
||||
}
|
||||
|
||||
export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||
type: CompileTypeMetadata;
|
||||
@ -505,24 +568,48 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||
this.pure = !!pure;
|
||||
}
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompilePipeSummary {
|
||||
return {isSummary: true, 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 */;
|
||||
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;
|
||||
exportedDirectives: CompileIdentifierMetadata[];
|
||||
exportedPipes: CompileIdentifierMetadata[];
|
||||
exportedModules: CompileNgModuleDirectiveSummary[];
|
||||
directiveLoaders: (() => Promise<void>)[];
|
||||
}
|
||||
|
||||
export type CompileNgModuleSummary =
|
||||
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a module.
|
||||
*/
|
||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
type: CompileTypeMetadata;
|
||||
declaredDirectives: CompileDirectiveMetadata[];
|
||||
exportedDirectives: CompileDirectiveMetadata[];
|
||||
declaredPipes: CompilePipeMetadata[];
|
||||
exportedPipes: CompilePipeMetadata[];
|
||||
// Note: See CompileDirectiveMetadata.entryComponents why this has to be a type.
|
||||
entryComponents: CompileTypeMetadata[];
|
||||
bootstrapComponents: CompileTypeMetadata[];
|
||||
declaredDirectives: CompileIdentifierMetadata[];
|
||||
exportedDirectives: CompileIdentifierMetadata[];
|
||||
declaredPipes: CompileIdentifierMetadata[];
|
||||
exportedPipes: CompileIdentifierMetadata[];
|
||||
entryComponents: CompileIdentifierMetadata[];
|
||||
bootstrapComponents: CompileIdentifierMetadata[];
|
||||
providers: CompileProviderMetadata[];
|
||||
|
||||
importedModules: CompileNgModuleMetadata[];
|
||||
exportedModules: CompileNgModuleMetadata[];
|
||||
importedModules: CompileNgModuleSummary[];
|
||||
exportedModules: CompileNgModuleSummary[];
|
||||
schemas: SchemaMetadata[];
|
||||
id: string;
|
||||
|
||||
@ -535,14 +622,14 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
type?: CompileTypeMetadata,
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
declaredDirectives?: CompileDirectiveMetadata[],
|
||||
exportedDirectives?: CompileDirectiveMetadata[],
|
||||
declaredPipes?: CompilePipeMetadata[],
|
||||
exportedPipes?: CompilePipeMetadata[],
|
||||
entryComponents?: CompileTypeMetadata[],
|
||||
bootstrapComponents?: CompileTypeMetadata[],
|
||||
importedModules?: CompileNgModuleMetadata[],
|
||||
exportedModules?: CompileNgModuleMetadata[],
|
||||
declaredDirectives?: CompileIdentifierMetadata[],
|
||||
exportedDirectives?: CompileIdentifierMetadata[],
|
||||
declaredPipes?: CompileIdentifierMetadata[],
|
||||
exportedPipes?: CompileIdentifierMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
bootstrapComponents?: CompileIdentifierMetadata[],
|
||||
importedModules?: CompileNgModuleSummary[],
|
||||
exportedModules?: CompileNgModuleSummary[],
|
||||
transitiveModule?: TransitiveCompileNgModuleMetadata,
|
||||
schemas?: SchemaMetadata[],
|
||||
id?: string
|
||||
@ -563,18 +650,54 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompileNgModuleSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
type: this.type,
|
||||
entryComponents: this.entryComponents,
|
||||
providers: this.providers,
|
||||
importedModules: this.importedModules,
|
||||
exportedModules: this.exportedModules,
|
||||
exportedDirectives: this.exportedDirectives,
|
||||
exportedPipes: this.exportedPipes,
|
||||
directiveLoaders: this.transitiveModule.directiveLoaders
|
||||
};
|
||||
}
|
||||
|
||||
toInjectorSummary(): CompileNgModuleInjectorSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
type: this.type,
|
||||
entryComponents: this.entryComponents,
|
||||
providers: this.providers,
|
||||
importedModules: this.importedModules,
|
||||
exportedModules: this.exportedModules
|
||||
};
|
||||
}
|
||||
toDirectiveSummary(): CompileNgModuleDirectiveSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
type: this.type,
|
||||
exportedDirectives: this.exportedDirectives,
|
||||
exportedPipes: this.exportedPipes,
|
||||
exportedModules: this.exportedModules,
|
||||
directiveLoaders: this.transitiveModule.directiveLoaders
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class TransitiveCompileNgModuleMetadata {
|
||||
directivesSet = new Set<Type<any>>();
|
||||
pipesSet = new Set<Type<any>>();
|
||||
directivesSet = new Set<any>();
|
||||
pipesSet = new Set<any>();
|
||||
|
||||
constructor(
|
||||
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
|
||||
public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[],
|
||||
public pipes: CompilePipeMetadata[]) {
|
||||
directives.forEach(dir => this.directivesSet.add(dir.type.reference));
|
||||
pipes.forEach(pipe => this.pipesSet.add(pipe.type.reference));
|
||||
public modules: CompileNgModuleInjectorSummary[], public providers: CompileProviderMetadata[],
|
||||
public entryComponents: CompileIdentifierMetadata[],
|
||||
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
|
||||
public directiveLoaders: (() => Promise<void>)[]) {
|
||||
directives.forEach(dir => this.directivesSet.add(dir.reference));
|
||||
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
|
||||
}
|
||||
}
|
||||
|
||||
@ -588,22 +711,13 @@ export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifi
|
||||
}
|
||||
});
|
||||
|
||||
return MapWrapper.values(map);
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
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>;
|
||||
|
@ -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[] {
|
||||
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
||||
let condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
||||
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||
]);
|
||||
if (evalResult.forceUpdate) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user