Compare commits
215 Commits
2.2.0-beta
...
2.2.x
Author | SHA1 | Date | |
---|---|---|---|
9a9a7ac7b5 | |||
423dd2898a | |||
ee2d6e572a | |||
ba8645c529 | |||
eba53fd16c | |||
c0698de2ea | |||
dc6728e7ad | |||
eb173bcd30 | |||
e39e16ae6a | |||
ff56da554f | |||
d160591453 | |||
380377139b | |||
9c7680ef69 | |||
69572ac2f1 | |||
76f53f929c | |||
ba52f2f252 | |||
e122f6bf0f | |||
453c758d1a | |||
015ca47336 | |||
f32e287812 | |||
9946ac5cc7 | |||
593e05dc97 | |||
da77b580c9 | |||
1733ea09bd | |||
1f4fa28fac | |||
c12e56ec0c | |||
4a5c8bd25f | |||
9c954740d1 | |||
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 |
197
CHANGELOG.md
197
CHANGELOG.md
@ -1,7 +1,191 @@
|
|||||||
<a name="2.2.0-beta.1"></a>
|
<a name="2.2.4"></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)
|
## [2.2.4](https://github.com/angular/angular/compare/2.2.3...2.2.4) (2016-11-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** update DatePipe to allow closure compilation ([eba53fd](https://github.com/angular/angular/commit/eba53fd))
|
||||||
|
* **compiler:** fix performance regression caused by 5b0f9e2 ([ee2d6e5](https://github.com/angular/angular/commit/ee2d6e5)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
||||||
|
* **compiler-cli:** fix paths in source maps to be relative ([eb173bc](https://github.com/angular/angular/commit/eb173bc)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.3"></a>
|
||||||
|
## [2.2.3](https://github.com/angular/angular/compare/2.2.2...2.2.3) (2016-11-23)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** Revert: fix versions of `@angular/tsc-wrapped` ([015ca47](https://github.com/angular/angular/commit/015ca47))
|
||||||
|
* **animations:** Revert: blend in all previously transitioned styles into next animation if interrupted ([c12e56e](https://github.com/angular/angular/commit/c12e56e))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.2"></a>
|
||||||
|
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||||
|
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
|
||||||
|
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
|
||||||
|
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
|
||||||
|
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
|
||||||
|
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
|
||||||
|
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
|
||||||
|
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
|
||||||
|
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||||
|
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.3.0-beta.0"></a>
|
||||||
|
# [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** assert xliff messages have translations ([7908679](https://github.com/angular/angular/commit/7908679)), closes [#12815](https://github.com/angular/angular/issues/12815) [#12604](https://github.com/angular/angular/issues/12604)
|
||||||
|
* **compiler:** updates hash algo for xmb/xtb files ([2f14415](https://github.com/angular/angular/commit/2f14415))
|
||||||
|
* **core:** fix placeholders handling in i18n. ([76e4911](https://github.com/angular/angular/commit/76e4911)), closes [#12512](https://github.com/angular/angular/issues/12512)
|
||||||
|
* **core:** misc i18n fixes ([ed5e98d](https://github.com/angular/angular/commit/ed5e98d))
|
||||||
|
* **core:** xmb serializer uses decimal messaged IDs ([08c038e](https://github.com/angular/angular/commit/08c038e)), closes [#12511](https://github.com/angular/angular/issues/12511)
|
||||||
|
* **platform-browser:** enable AOT ([efbbefd](https://github.com/angular/angular/commit/efbbefd)), closes [#12783](https://github.com/angular/angular/issues/12783)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **core:** add `attachView` / `detachView` to ApplicationRef ([9f7d32a](https://github.com/angular/angular/commit/9f7d32a)), closes [#9293](https://github.com/angular/angular/issues/9293)
|
||||||
|
* **core:** expose `ViewRef` as `ChangeDetectorRef` ([1b5384e](https://github.com/angular/angular/commit/1b5384e)), closes [#12722](https://github.com/angular/angular/issues/12722)
|
||||||
|
* **core:** implements a decimal fingerprint for i18n ([582550a](https://github.com/angular/angular/commit/582550a))
|
||||||
|
* **router:** register router with ngprobe ([c2fae72](https://github.com/angular/angular/commit/c2fae72))
|
||||||
|
* **router_link:** add skipLocationChange and replaceUrl inputs ([#12850](https://github.com/angular/angular/issues/12850)) ([46d1502](https://github.com/angular/angular/commit/46d1502))
|
||||||
|
|
||||||
|
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
|
||||||
|
|
||||||
|
<a name="2.2.1"></a>
|
||||||
|
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** only pass in same typed players as previous players into web-animations ([#12907](https://github.com/angular/angular/issues/12907)) ([583d283](https://github.com/angular/angular/commit/583d283))
|
||||||
|
* **animations:** retain styling when transition destinations are changed ([#12208](https://github.com/angular/angular/issues/12208)) ([5c46c49](https://github.com/angular/angular/commit/5c46c49)), closes [#9661](https://github.com/angular/angular/issues/9661)
|
||||||
|
* **core:** support `ngTemplateOutlet` in production mode ([#12921](https://github.com/angular/angular/issues/12921)) ([4628798](https://github.com/angular/angular/commit/4628798)), closes [#12911](https://github.com/angular/angular/issues/12911)
|
||||||
|
* **http:** correctly handle response body for 204 status code ([21a4de9](https://github.com/angular/angular/commit/21a4de9)), closes [#12830](https://github.com/angular/angular/issues/12830) [#12393](https://github.com/angular/angular/issues/12393)
|
||||||
|
* **http:** return request url if it cannot be retrieved from response ([845ea23](https://github.com/angular/angular/commit/845ea23)), closes [#12837](https://github.com/angular/angular/issues/12837)
|
||||||
|
* **upgrade:** make AoT ngUpgrade work with the testability API and resumeBootstrap() ([#12910](https://github.com/angular/angular/issues/12910)) ([dc1662a](https://github.com/angular/angular/commit/dc1662a))
|
||||||
|
* **platform-browser:** fix disableDebugTools() ([#12918](https://github.com/angular/angular/issues/12918)) ([7b67bad](https://github.com/angular/angular/commit/7b67bad))
|
||||||
|
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([364642d](https://github.com/angular/angular/commit/364642d))
|
||||||
|
* **router:** removes a peer dependency from router to upgrade ([1dcf1f4](https://github.com/angular/angular/commit/1dcf1f4))
|
||||||
|
* **forms** allow for null values in HTML select options bound with ngValue ([e0ce545](https://github.com/angular/angular/commit/e0ce545)), closes [#10349](https://github.com/angular/angular/issues/10349)
|
||||||
|
* **router:** should not create a route state if navigation is canceled ([#12868](https://github.com/angular/angular/issues/12868)) ([dabaf85](https://github.com/angular/angular/commit/dabaf85)), closes [#12776](https://github.com/angular/angular/issues/12776)
|
||||||
|
* **common:** select should allow for null values in HTML select options bound with ngValue ([e02c180](https://github.com/angular/angular/commit/e02c180)), closes [#12829](https://github.com/angular/angular/issues/12829)
|
||||||
|
* **compiler-cli:** support ctorParams in function closure ([#12876](https://github.com/angular/angular/issues/12876)) ([6cdc3b5](https://github.com/angular/angular/commit/6cdc3b5))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.0"></a>
|
||||||
|
# [2.2.0 upgrade-firebooster](https://github.com/angular/angular/compare/2.2.0-rc.0...2.2.0) (2016-11-14)
|
||||||
|
|
||||||
|
### 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
|
### 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)
|
* **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 +202,10 @@
|
|||||||
* **router:** preserve resolve data ([6ccbfd4](https://github.com/angular/angular/commit/6ccbfd4)), closes [#12306](https://github.com/angular/angular/issues/12306)
|
* **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
|
### Code Refactoring
|
||||||
|
|
||||||
* **upgrade:** re-export the new static upgrade APIs on new entry ([a26dd28](https://github.com/angular/angular/commit/a26dd28))
|
* **upgrade:** re-export the new static upgrade APIs on new entry ([a26dd28](https://github.com/angular/angular/commit/a26dd28))
|
||||||
@ -28,13 +216,16 @@
|
|||||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
* **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:
|
* upgrade: Four newly added APIs in 2.2.0-beta:
|
||||||
downgradeComponent, downgradeInjectable, UpgradeComponent, and UpgradeModule are no longer exported by @angular/upgrade.
|
downgradeComponent, downgradeInjectable, UpgradeComponent, and UpgradeModule are no longer exported by @angular/upgrade.
|
||||||
Import these from @angular/upgrade/static instead.
|
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)
|
# [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?
|
## <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:
|
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
|
||||||
platform-webworker-dynamic
|
platform-webworker-dynamic
|
||||||
http
|
http
|
||||||
router
|
|
||||||
upgrade
|
upgrade
|
||||||
|
router
|
||||||
compiler-cli
|
compiler-cli
|
||||||
benchpress)
|
benchpress)
|
||||||
BUILD_ALL=true
|
BUILD_ALL=true
|
||||||
BUNDLE=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
|
for ARG in "$@"; do
|
||||||
case "$ARG" in
|
case "$ARG" in
|
||||||
@ -31,6 +35,10 @@ for ARG in "$@"; do
|
|||||||
--bundle=*)
|
--bundle=*)
|
||||||
BUNDLE=( "${ARG#--bundle=}" )
|
BUNDLE=( "${ARG#--bundle=}" )
|
||||||
;;
|
;;
|
||||||
|
--publish)
|
||||||
|
VERSION_SUFFIX=""
|
||||||
|
REMOVE_BENCHPRESS=true
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option $ARG."
|
echo "Unknown option $ARG."
|
||||||
exit 1
|
exit 1
|
||||||
@ -38,6 +46,10 @@ for ARG in "$@"; do
|
|||||||
esac
|
esac
|
||||||
done
|
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
|
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"
|
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
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/zone.js .
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-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/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/reflect-metadata/Reflect.js .
|
||||||
ln -s ../../../../node_modules/rxjs .
|
ln -s ../../../../node_modules/rxjs .
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
|
ln -s ../../../../node_modules/hammerjs/hammer.js .
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
echo "====== Copying files needed for benchmarks ====="
|
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/zone.js .
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-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/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/reflect-metadata/Reflect.js .
|
||||||
ln -s ../../../../node_modules/rxjs .
|
ln -s ../../../../node_modules/rxjs .
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
@ -102,15 +114,28 @@ do
|
|||||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||||
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
||||||
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
||||||
|
UMD_UPGRADE_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.js
|
||||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||||
UMD_STATIC_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-static.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}
|
rm -rf ${DESTDIR}
|
||||||
|
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||||
|
|
||||||
|
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 ${SRCDIR}/package.json ${DESTDIR}/
|
||||||
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||||
|
|
||||||
@ -171,9 +196,37 @@ do
|
|||||||
mv ${UMD_STATIC_ES5_PATH}.tmp ${UMD_STATIC_ES5_PATH}
|
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}
|
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_STATIC_ES5_MIN_PATH} ${UMD_STATIC_ES5_PATH}
|
||||||
fi
|
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
|
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
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "====== Building examples: ./modules/@angular/examples/build.sh ====="
|
||||||
./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:
|
dependencies:
|
||||||
pre:
|
pre:
|
||||||
- npm install -g npm
|
- npm install -g npm@3.6.0
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
31
gulpfile.js
31
gulpfile.js
@ -125,38 +125,31 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
|
|||||||
.on('close', 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
|
// 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');
|
const tslint = require('gulp-tslint');
|
||||||
// Built-in rules are at
|
// Built-in rules are at
|
||||||
// https://github.com/palantir/tslint#supported-rules
|
// https://palantir.github.io/tslint/rules/
|
||||||
const tslintConfig = require('./tslint.json');
|
const tslintConfig = require('./tslint.json');
|
||||||
return gulp
|
return gulp
|
||||||
.src([
|
.src([
|
||||||
// todo(vicb): add .js files when supported
|
// todo(vicb): add .js files when supported
|
||||||
// see https://github.com/palantir/tslint/pull/1515
|
// see https://github.com/palantir/tslint/pull/1515
|
||||||
'modules/@angular/**/*.ts',
|
'./modules/**/*.ts',
|
||||||
'modules/benchpress/**/*.ts',
|
'./tools/**/*.ts',
|
||||||
'./*.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({
|
.pipe(tslint({
|
||||||
tslint: require('tslint').default,
|
tslint: require('tslint').default,
|
||||||
configuration: tslintConfig,
|
configuration: tslintConfig,
|
||||||
rulesDirectory: 'dist/tools/tslint',
|
|
||||||
formatter: 'prose',
|
formatter: 'prose',
|
||||||
}))
|
}))
|
||||||
.pipe(tslint.report({emitError: true}));
|
.pipe(tslint.report({emitError: true}));
|
||||||
|
@ -90,17 +90,17 @@ module.exports = function(config) {
|
|||||||
project: 'Angular2',
|
project: 'Angular2',
|
||||||
startTunnel: false,
|
startTunnel: false,
|
||||||
retryLimit: 3,
|
retryLimit: 3,
|
||||||
timeout: 600,
|
timeout: 1800,
|
||||||
pollingTimeout: 10000,
|
pollingTimeout: 10000,
|
||||||
},
|
},
|
||||||
|
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
port: 9876,
|
port: 9876,
|
||||||
captureTimeout: 60000,
|
captureTimeout: 180000,
|
||||||
browserDisconnectTimeout: 60000,
|
browserDisconnectTimeout: 180000,
|
||||||
browserDisconnectTolerance: 3,
|
browserDisconnectTolerance: 3,
|
||||||
browserNoActivityTimeout: 60000,
|
browserNoActivityTimeout: 300000,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.TRAVIS) {
|
if (process.env.TRAVIS) {
|
||||||
|
@ -10,7 +10,7 @@ declare var exportFunction: any;
|
|||||||
declare var unsafeWindow: any;
|
declare var unsafeWindow: any;
|
||||||
|
|
||||||
exportFunction(function() {
|
exportFunction(function() {
|
||||||
var curTime = unsafeWindow.performance.now();
|
const curTime = unsafeWindow.performance.now();
|
||||||
(<any>self).port.emit('startProfiler', curTime);
|
(<any>self).port.emit('startProfiler', curTime);
|
||||||
}, unsafeWindow, {defineAs: 'startProfiler'});
|
}, unsafeWindow, {defineAs: 'startProfiler'});
|
||||||
|
|
||||||
@ -28,11 +28,11 @@ exportFunction(function() {
|
|||||||
}, unsafeWindow, {defineAs: 'forceGC'});
|
}, unsafeWindow, {defineAs: 'forceGC'});
|
||||||
|
|
||||||
exportFunction(function(name: string) {
|
exportFunction(function(name: string) {
|
||||||
var curTime = unsafeWindow.performance.now();
|
const curTime = unsafeWindow.performance.now();
|
||||||
(<any>self).port.emit('markStart', name, curTime);
|
(<any>self).port.emit('markStart', name, curTime);
|
||||||
}, unsafeWindow, {defineAs: 'markStart'});
|
}, unsafeWindow, {defineAs: 'markStart'});
|
||||||
|
|
||||||
exportFunction(function(name: string) {
|
exportFunction(function(name: string) {
|
||||||
var curTime = unsafeWindow.performance.now();
|
const curTime = unsafeWindow.performance.now();
|
||||||
(<any>self).port.emit('markEnd', name, curTime);
|
(<any>self).port.emit('markEnd', name, curTime);
|
||||||
}, unsafeWindow, {defineAs: 'markEnd'});
|
}, unsafeWindow, {defineAs: 'markEnd'});
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var {Cc, Ci, Cu} = require('chrome');
|
const {Cc, Ci, Cu} = require('chrome');
|
||||||
var os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
const os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||||
var ParserUtil = require('./parser_util');
|
const ParserUtil = require('./parser_util');
|
||||||
|
|
||||||
class Profiler {
|
class Profiler {
|
||||||
private _profiler: any;
|
private _profiler: any;
|
||||||
@ -26,8 +26,8 @@ class Profiler {
|
|||||||
stop() { this._profiler.StopProfiler(); }
|
stop() { this._profiler.StopProfiler(); }
|
||||||
|
|
||||||
getProfilePerfEvents() {
|
getProfilePerfEvents() {
|
||||||
var profileData = this._profiler.getProfileData();
|
const profileData = this._profiler.getProfileData();
|
||||||
var perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
let perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
||||||
perfEvents = this._mergeMarkerEvents(perfEvents);
|
perfEvents = this._mergeMarkerEvents(perfEvents);
|
||||||
perfEvents.sort(function(event1: any, event2: any) {
|
perfEvents.sort(function(event1: any, event2: any) {
|
||||||
return event1.ts - event2.ts;
|
return event1.ts - event2.ts;
|
||||||
@ -55,9 +55,9 @@ function forceGC() {
|
|||||||
os.notifyObservers(null, 'child-gc-request', null);
|
os.notifyObservers(null, 'child-gc-request', null);
|
||||||
};
|
};
|
||||||
|
|
||||||
var mod = require('sdk/page-mod');
|
const mod = require('sdk/page-mod');
|
||||||
var data = require('sdk/self').data;
|
const data = require('sdk/self').data;
|
||||||
var profiler = new Profiler();
|
const profiler = new Profiler();
|
||||||
mod.PageMod({
|
mod.PageMod({
|
||||||
include: ['*'],
|
include: ['*'],
|
||||||
contentScriptFile: data.url('installed_script.js'),
|
contentScriptFile: data.url('installed_script.js'),
|
||||||
|
@ -12,11 +12,11 @@
|
|||||||
* within the perf profile.
|
* within the perf profile.
|
||||||
*/
|
*/
|
||||||
export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||||
var inProgressEvents = new Map(); // map from event name to start time
|
const inProgressEvents = new Map(); // map from event name to start time
|
||||||
var finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
const finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
||||||
var addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
const addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
||||||
var categorizedEventName = categorizeEvent(eventName);
|
const categorizedEventName = categorizeEvent(eventName);
|
||||||
var args: {[key: string]: any} = undefined;
|
let args: {[key: string]: any} = undefined;
|
||||||
if (categorizedEventName == 'gc') {
|
if (categorizedEventName == 'gc') {
|
||||||
// TODO: We cannot measure heap size at the moment
|
// TODO: We cannot measure heap size at the moment
|
||||||
args = {usedHeapSize: 0};
|
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
|
// 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
|
// we go through all the samples and construct the start and end time for each
|
||||||
// event.
|
// event.
|
||||||
for (var i = 0; i < samples.length; ++i) {
|
for (let i = 0; i < samples.length; ++i) {
|
||||||
var sample = samples[i];
|
const sample = samples[i];
|
||||||
var sampleTime = sample.time;
|
const sampleTime = sample.time;
|
||||||
|
|
||||||
// Add all the frames into a set so it's easier/faster to find the set
|
// Add all the frames into a set so it's easier/faster to find the set
|
||||||
// differences
|
// differences
|
||||||
var sampleFrames = new Set();
|
const sampleFrames = new Set();
|
||||||
sample.frames.forEach(function(frame: {[key: string]: any}) {
|
sample.frames.forEach(function(frame: {[key: string]: any}) {
|
||||||
sampleFrames.add(frame['location']);
|
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,
|
// 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
|
// then it must have just finished. We add this event to the finishedEvents
|
||||||
// array and remove it from the inProgressEvents map.
|
// 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) {
|
inProgressEvents.forEach(function(startTime, eventName) {
|
||||||
if (!(sampleFrames.has(eventName))) {
|
if (!(sampleFrames.has(eventName))) {
|
||||||
addFinishedEvent(eventName, startTime, previousSampleTime);
|
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
|
// If anything is still in progress, we need to included it as a finished event
|
||||||
// since recording ended.
|
// since recording ended.
|
||||||
var lastSampleTime = samples[samples.length - 1].time;
|
const lastSampleTime = samples[samples.length - 1].time;
|
||||||
inProgressEvents.forEach(function(startTime, eventName) {
|
inProgressEvents.forEach(function(startTime, eventName) {
|
||||||
addFinishedEvent(eventName, startTime, lastSampleTime);
|
addFinishedEvent(eventName, startTime, lastSampleTime);
|
||||||
});
|
});
|
||||||
|
@ -6,15 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var q = require('q');
|
const q = require('q');
|
||||||
var FirefoxProfile = require('firefox-profile');
|
const FirefoxProfile = require('firefox-profile');
|
||||||
var jpm = require('jpm/lib/xpi');
|
const jpm = require('jpm/lib/xpi');
|
||||||
var pathUtil = require('path');
|
const pathUtil = require('path');
|
||||||
|
|
||||||
var PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
const PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
||||||
|
|
||||||
exports.getAbsolutePath = function(path: string) {
|
exports.getAbsolutePath = function(path: string) {
|
||||||
var normalizedPath = pathUtil.normalize(path);
|
const normalizedPath = pathUtil.normalize(path);
|
||||||
if (pathUtil.resolve(normalizedPath) == normalizedPath) {
|
if (pathUtil.resolve(normalizedPath) == normalizedPath) {
|
||||||
// Already absolute path
|
// Already absolute path
|
||||||
return normalizedPath;
|
return normalizedPath;
|
||||||
@ -24,12 +24,12 @@ exports.getAbsolutePath = function(path: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.getFirefoxProfile = function(extensionPath: 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.addExtensions([extensionPath], () => {
|
||||||
firefoxProfile.encoded((encodedProfile: any) => {
|
firefoxProfile.encoded((encodedProfile: any) => {
|
||||||
var multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
const multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||||
deferred.resolve(multiCapabilities);
|
deferred.resolve(multiCapabilities);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -38,10 +38,10 @@ exports.getFirefoxProfile = function(extensionPath: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.getFirefoxProfileWithExtension = function() {
|
exports.getFirefoxProfileWithExtension = function() {
|
||||||
var absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
const absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
||||||
var packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
const packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
||||||
|
|
||||||
var savedCwd = process.cwd();
|
const savedCwd = process.cwd();
|
||||||
process.chdir(absPackageJsonDir);
|
process.chdir(absPackageJsonDir);
|
||||||
|
|
||||||
return jpm(packageJson).then((xpiPath: string) => {
|
return jpm(packageJson).then((xpiPath: string) => {
|
||||||
|
@ -55,9 +55,9 @@ export class MultiMetric extends Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
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]; }); });
|
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
||||||
return result;
|
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} {
|
describe(): {[key: string]: string} {
|
||||||
var res: {[key: string]: any} = {
|
const res: {[key: string]: any} = {
|
||||||
'scriptTime': 'script execution time in ms, including gc and render',
|
'scriptTime': 'script execution time in ms, including gc and render',
|
||||||
'pureScriptTime': 'script execution time in ms, without gc nor 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._captureFrames) {
|
||||||
if (!this._perfLogFeatures.frameCapture) {
|
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
|
// using dot syntax for metric name to keep them grouped together in console reporter
|
||||||
res['frameTime.mean'] = warningMsg;
|
res['frameTime.mean'] = warningMsg;
|
||||||
res['frameTime.worst'] = warningMsg;
|
res['frameTime.worst'] = warningMsg;
|
||||||
@ -93,14 +93,14 @@ export class PerflogMetric extends Metric {
|
|||||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
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];
|
res[name] = this._microMetrics[name];
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginMeasure(): Promise<any> {
|
beginMeasure(): Promise<any> {
|
||||||
var resultPromise = Promise.resolve(null);
|
let resultPromise = Promise.resolve(null);
|
||||||
if (this._forceGc) {
|
if (this._forceGc) {
|
||||||
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ export class PerflogMetric extends Metric {
|
|||||||
private _endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
private _endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
||||||
return this._endMeasure(true).then((measureValues) => {
|
return this._endMeasure(true).then((measureValues) => {
|
||||||
// disable frame capture for measurements during forced gc
|
// disable frame capture for measurements during forced gc
|
||||||
var originalFrameCaptureValue = this._captureFrames;
|
const originalFrameCaptureValue = this._captureFrames;
|
||||||
this._captureFrames = false;
|
this._captureFrames = false;
|
||||||
return this._driverExtension.gc()
|
return this._driverExtension.gc()
|
||||||
.then((_) => this._endMeasure(restartMeasure))
|
.then((_) => this._endMeasure(restartMeasure))
|
||||||
@ -137,8 +137,8 @@ export class PerflogMetric extends Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _endMeasure(restart: boolean): Promise<{[key: string]: number}> {
|
private _endMeasure(restart: boolean): Promise<{[key: string]: number}> {
|
||||||
var markName = this._markName(this._measureCount - 1);
|
const markName = this._markName(this._measureCount - 1);
|
||||||
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
const nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||||
return this._driverExtension.timeEnd(markName, nextMarkName)
|
return this._driverExtension.timeEnd(markName, nextMarkName)
|
||||||
.then((_) => this._readUntilEndMark(markName));
|
.then((_) => this._readUntilEndMark(markName));
|
||||||
}
|
}
|
||||||
@ -150,26 +150,26 @@ export class PerflogMetric extends Metric {
|
|||||||
}
|
}
|
||||||
return this._driverExtension.readPerfLog().then((events) => {
|
return this._driverExtension.readPerfLog().then((events) => {
|
||||||
this._addEvents(events);
|
this._addEvents(events);
|
||||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
const result = this._aggregateEvents(this._remainingEvents, markName);
|
||||||
if (result) {
|
if (result) {
|
||||||
this._remainingEvents = events;
|
this._remainingEvents = events;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
var resolve: (result: any) => void;
|
let resolve: (result: any) => void;
|
||||||
var promise = new Promise(res => { resolve = res; });
|
const promise = new Promise(res => { resolve = res; });
|
||||||
this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
|
this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addEvents(events: PerfLogEvent[]) {
|
private _addEvents(events: PerfLogEvent[]) {
|
||||||
var needSort = false;
|
let needSort = false;
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
if (event['ph'] === 'X') {
|
if (event['ph'] === 'X') {
|
||||||
needSort = true;
|
needSort = true;
|
||||||
var startEvent: PerfLogEvent = {};
|
const startEvent: PerfLogEvent = {};
|
||||||
var endEvent: PerfLogEvent = {};
|
const endEvent: PerfLogEvent = {};
|
||||||
for (let prop in event) {
|
for (const prop in event) {
|
||||||
startEvent[prop] = event[prop];
|
startEvent[prop] = event[prop];
|
||||||
endEvent[prop] = event[prop];
|
endEvent[prop] = event[prop];
|
||||||
}
|
}
|
||||||
@ -185,14 +185,14 @@ export class PerflogMetric extends Metric {
|
|||||||
if (needSort) {
|
if (needSort) {
|
||||||
// Need to sort because of the ph==='X' events
|
// Need to sort because of the ph==='X' events
|
||||||
this._remainingEvents.sort((a, b) => {
|
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;
|
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _aggregateEvents(events: PerfLogEvent[], markName: string): {[key: string]: number} {
|
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) {
|
if (this._perfLogFeatures.gc) {
|
||||||
result['gcTime'] = 0;
|
result['gcTime'] = 0;
|
||||||
result['majorGcTime'] = 0;
|
result['majorGcTime'] = 0;
|
||||||
@ -207,7 +207,7 @@ export class PerflogMetric extends Metric {
|
|||||||
result['frameTime.worst'] = 0;
|
result['frameTime.worst'] = 0;
|
||||||
result['frameTime.smooth'] = 0;
|
result['frameTime.smooth'] = 0;
|
||||||
}
|
}
|
||||||
for (let name in this._microMetrics) {
|
for (const name in this._microMetrics) {
|
||||||
result[name] = 0;
|
result[name] = 0;
|
||||||
}
|
}
|
||||||
if (this._receivedData) {
|
if (this._receivedData) {
|
||||||
@ -217,11 +217,11 @@ export class PerflogMetric extends Metric {
|
|||||||
result['requestCount'] = 0;
|
result['requestCount'] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var markStartEvent: PerfLogEvent = null;
|
let markStartEvent: PerfLogEvent = null;
|
||||||
var markEndEvent: PerfLogEvent = null;
|
let markEndEvent: PerfLogEvent = null;
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
var ph = event['ph'];
|
const ph = event['ph'];
|
||||||
var name = event['name'];
|
const name = event['name'];
|
||||||
if (ph === 'B' && name === markName) {
|
if (ph === 'B' && name === markName) {
|
||||||
markStartEvent = event;
|
markStartEvent = event;
|
||||||
} else if (ph === 'I' && name === 'navigationStart') {
|
} else if (ph === 'I' && name === 'navigationStart') {
|
||||||
@ -237,23 +237,23 @@ export class PerflogMetric extends Metric {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var gcTimeInScript = 0;
|
let gcTimeInScript = 0;
|
||||||
var renderTimeInScript = 0;
|
let renderTimeInScript = 0;
|
||||||
|
|
||||||
var frameTimestamps: number[] = [];
|
const frameTimestamps: number[] = [];
|
||||||
var frameTimes: number[] = [];
|
const frameTimes: number[] = [];
|
||||||
var frameCaptureStartEvent: PerfLogEvent = null;
|
let frameCaptureStartEvent: PerfLogEvent = null;
|
||||||
var frameCaptureEndEvent: PerfLogEvent = null;
|
let frameCaptureEndEvent: PerfLogEvent = null;
|
||||||
|
|
||||||
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
const intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||||
var intervalStartCount: {[key: string]: number} = {};
|
const intervalStartCount: {[key: string]: number} = {};
|
||||||
|
|
||||||
var inMeasureRange = false;
|
let inMeasureRange = false;
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
var ph = event['ph'];
|
const ph = event['ph'];
|
||||||
var name = event['name'];
|
let name = event['name'];
|
||||||
var microIterations = 1;
|
let microIterations = 1;
|
||||||
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
const microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||||
if (microIterationsMatch) {
|
if (microIterationsMatch) {
|
||||||
name = microIterationsMatch[1];
|
name = microIterationsMatch[1];
|
||||||
microIterations = parseInt(microIterationsMatch[2], 10);
|
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||||
@ -307,15 +307,15 @@ export class PerflogMetric extends Metric {
|
|||||||
} else if ((ph === 'E') && intervalStarts[name]) {
|
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||||
intervalStartCount[name]--;
|
intervalStartCount[name]--;
|
||||||
if (intervalStartCount[name] === 0) {
|
if (intervalStartCount[name] === 0) {
|
||||||
var startEvent = intervalStarts[name];
|
const startEvent = intervalStarts[name];
|
||||||
var duration = (event['ts'] - startEvent['ts']);
|
const duration = (event['ts'] - startEvent['ts']);
|
||||||
intervalStarts[name] = null;
|
intervalStarts[name] = null;
|
||||||
if (name === 'gc') {
|
if (name === 'gc') {
|
||||||
result['gcTime'] += duration;
|
result['gcTime'] += duration;
|
||||||
var amount =
|
const amount =
|
||||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||||
result['gcAmount'] += amount;
|
result['gcAmount'] += amount;
|
||||||
var majorGc = event['args']['majorGc'];
|
const majorGc = event['args']['majorGc'];
|
||||||
if (majorGc && majorGc) {
|
if (majorGc && majorGc) {
|
||||||
result['majorGcTime'] += duration;
|
result['majorGcTime'] += duration;
|
||||||
}
|
}
|
||||||
@ -351,7 +351,7 @@ export class PerflogMetric extends Metric {
|
|||||||
|
|
||||||
private _addFrameMetrics(result: {[key: string]: number}, frameTimes: any[]) {
|
private _addFrameMetrics(result: {[key: string]: number}, frameTimes: any[]) {
|
||||||
result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
|
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.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
|
||||||
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
|
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
|
||||||
result['frameTime.smooth'] =
|
result['frameTime.smooth'] =
|
||||||
@ -361,11 +361,11 @@ export class PerflogMetric extends Metric {
|
|||||||
private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; }
|
private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
const _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
||||||
|
|
||||||
var _MAX_RETRY_COUNT = 20;
|
const _MAX_RETRY_COUNT = 20;
|
||||||
var _MARK_NAME_PREFIX = 'benchpress';
|
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
|
// 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}> {
|
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||||
let resolve: (result: any) => void;
|
let resolve: (result: any) => void;
|
||||||
let reject: (error: any) => void;
|
let reject: (error: any) => void;
|
||||||
let promise = new Promise((res, rej) => {
|
const promise = new Promise((res, rej) => {
|
||||||
resolve = res;
|
resolve = res;
|
||||||
reject = rej;
|
reject = rej;
|
||||||
});
|
});
|
||||||
let adapter = this._wdAdapter;
|
const adapter = this._wdAdapter;
|
||||||
let names = Object.keys(this._userMetrics);
|
const names = Object.keys(this._userMetrics);
|
||||||
|
|
||||||
function getAndClearValues() {
|
function getAndClearValues() {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
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')) {
|
if (values.every(v => typeof v === 'number')) {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||||
.then((_: any[]) => {
|
.then((_: any[]) => {
|
||||||
let map: {[k: string]: any} = {};
|
const map: {[k: string]: any} = {};
|
||||||
for (let i = 0, n = names.length; i < n; i++) {
|
for (let i = 0, n = names.length; i < n; i++) {
|
||||||
map[names[i]] = values[i];
|
map[names[i]] = values[i];
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,8 @@ export class ConsoleReporter extends Reporter {
|
|||||||
];
|
];
|
||||||
|
|
||||||
private static _lpad(value: string, columnWidth: number, fill = ' ') {
|
private static _lpad(value: string, columnWidth: number, fill = ' ') {
|
||||||
var result = '';
|
let result = '';
|
||||||
for (var i = 0; i < columnWidth - value.length; i++) {
|
for (let i = 0; i < columnWidth - value.length; i++) {
|
||||||
result += fill;
|
result += fill;
|
||||||
}
|
}
|
||||||
return result + value;
|
return result + value;
|
||||||
@ -49,7 +49,7 @@ export class ConsoleReporter extends Reporter {
|
|||||||
private _printDescription(sampleDescription: SampleDescription) {
|
private _printDescription(sampleDescription: SampleDescription) {
|
||||||
this._print(`BENCHMARK ${sampleDescription.id}`);
|
this._print(`BENCHMARK ${sampleDescription.id}`);
|
||||||
this._print('Description:');
|
this._print('Description:');
|
||||||
var props = sortedProps(sampleDescription.description);
|
const props = sortedProps(sampleDescription.description);
|
||||||
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
||||||
this._print('Metrics:');
|
this._print('Metrics:');
|
||||||
this._metricNames.forEach((metricName) => {
|
this._metricNames.forEach((metricName) => {
|
||||||
@ -61,8 +61,8 @@ export class ConsoleReporter extends Reporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
||||||
var formattedValues = this._metricNames.map(metricName => {
|
const formattedValues = this._metricNames.map(metricName => {
|
||||||
var value = measureValues.values[metricName];
|
const value = measureValues.values[metricName];
|
||||||
return formatNum(value);
|
return formatNum(value);
|
||||||
});
|
});
|
||||||
this._printStringRow(formattedValues);
|
this._printStringRow(formattedValues);
|
||||||
|
@ -38,7 +38,7 @@ export class JsonFileReporter extends Reporter {
|
|||||||
sortedProps(this._description.metrics).forEach((metricName) => {
|
sortedProps(this._description.metrics).forEach((metricName) => {
|
||||||
stats[metricName] = formatStats(validSample, metricName);
|
stats[metricName] = formatStats(validSample, metricName);
|
||||||
});
|
});
|
||||||
var content = JSON.stringify(
|
const content = JSON.stringify(
|
||||||
{
|
{
|
||||||
'description': this._description,
|
'description': this._description,
|
||||||
'stats': stats,
|
'stats': stats,
|
||||||
@ -46,7 +46,7 @@ export class JsonFileReporter extends Reporter {
|
|||||||
'validSample': validSample,
|
'validSample': validSample,
|
||||||
},
|
},
|
||||||
null, 2);
|
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);
|
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 {
|
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
||||||
var samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
const samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
||||||
var mean = Statistic.calculateMean(samples);
|
const mean = Statistic.calculateMean(samples);
|
||||||
var cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
const cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
||||||
var formattedMean = formatNum(mean);
|
const formattedMean = formatNum(mean);
|
||||||
// Note: Don't use the unicode character for +- as it might cause
|
// Note: Don't use the unicode character for +- as it might cause
|
||||||
// hickups for consoles...
|
// hickups for consoles...
|
||||||
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||||
|
@ -45,7 +45,7 @@ export class Runner {
|
|||||||
providers?: Provider[],
|
providers?: Provider[],
|
||||||
userMetrics?: {[key: string]: string}
|
userMetrics?: {[key: string]: string}
|
||||||
}): Promise<SampleState> {
|
}): Promise<SampleState> {
|
||||||
var sampleProviders: Provider[] = [
|
const sampleProviders: Provider[] = [
|
||||||
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
||||||
{provide: Options.EXECUTE, useValue: execute}
|
{provide: Options.EXECUTE, useValue: execute}
|
||||||
];
|
];
|
||||||
@ -62,33 +62,33 @@ export class Runner {
|
|||||||
sampleProviders.push(providers);
|
sampleProviders.push(providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
var inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
const inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||||
var adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
const adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
var capabilities = args[0];
|
const capabilities = args[0];
|
||||||
var userAgent = args[1];
|
const userAgent = args[1];
|
||||||
|
|
||||||
// This might still create instances twice. We are creating a new injector with all the
|
// This might still create instances twice. We are creating a new injector with all the
|
||||||
// providers.
|
// providers.
|
||||||
// Only WebDriverAdapter is reused.
|
// Only WebDriverAdapter is reused.
|
||||||
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
||||||
// injectors are handled better.
|
// injectors are handled better.
|
||||||
var injector = ReflectiveInjector.resolveAndCreate([
|
const injector = ReflectiveInjector.resolveAndCreate([
|
||||||
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
||||||
{provide: Options.USER_AGENT, useValue: userAgent},
|
{provide: Options.USER_AGENT, useValue: userAgent},
|
||||||
{provide: WebDriverAdapter, useValue: adapter}
|
{provide: WebDriverAdapter, useValue: adapter}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var sampler = injector.get(Sampler);
|
const sampler = injector.get(Sampler);
|
||||||
return sampler.sample();
|
return sampler.sample();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _DEFAULT_PROVIDERS = [
|
const _DEFAULT_PROVIDERS = [
|
||||||
Options.DEFAULT_PROVIDERS,
|
Options.DEFAULT_PROVIDERS,
|
||||||
Sampler.PROVIDERS,
|
Sampler.PROVIDERS,
|
||||||
ConsoleReporter.PROVIDERS,
|
ConsoleReporter.PROVIDERS,
|
||||||
|
@ -49,7 +49,7 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||||
var resultPromise: Promise<SampleState>;
|
let resultPromise: Promise<SampleState>;
|
||||||
if (this._prepare !== Options.NO_PREPARE) {
|
if (this._prepare !== Options.NO_PREPARE) {
|
||||||
resultPromise = this._driver.waitFor(this._prepare);
|
resultPromise = this._driver.waitFor(this._prepare);
|
||||||
} else {
|
} else {
|
||||||
@ -64,10 +64,10 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _report(state: SampleState, metricValues: {[key: string]: any}): Promise<SampleState> {
|
private _report(state: SampleState, metricValues: {[key: string]: any}): Promise<SampleState> {
|
||||||
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
const measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||||
var completeSample = state.completeSample.concat([measureValues]);
|
const completeSample = state.completeSample.concat([measureValues]);
|
||||||
var validSample = this._validator.validate(completeSample);
|
const validSample = this._validator.validate(completeSample);
|
||||||
var resultPromise = this._reporter.reportMeasureValues(measureValues);
|
let resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||||
if (isPresent(validSample)) {
|
if (isPresent(validSample)) {
|
||||||
resultPromise =
|
resultPromise =
|
||||||
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample));
|
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample));
|
||||||
|
@ -12,14 +12,14 @@ export class Statistic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static calculateMean(samples: number[]) {
|
static calculateMean(samples: number[]) {
|
||||||
var total = 0;
|
let total = 0;
|
||||||
// TODO: use reduce
|
// TODO: use reduce
|
||||||
samples.forEach(x => total += x);
|
samples.forEach(x => total += x);
|
||||||
return total / samples.length;
|
return total / samples.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
static calculateStandardDeviation(samples: number[], mean: number) {
|
static calculateStandardDeviation(samples: number[], mean: number) {
|
||||||
var deviation = 0;
|
let deviation = 0;
|
||||||
// TODO: use reduce
|
// TODO: use reduce
|
||||||
samples.forEach(x => deviation += Math.pow(x - mean, 2));
|
samples.forEach(x => deviation += Math.pow(x - mean, 2));
|
||||||
deviation = deviation / (samples.length);
|
deviation = deviation / (samples.length);
|
||||||
@ -30,9 +30,9 @@ export class Statistic {
|
|||||||
static calculateRegressionSlope(
|
static calculateRegressionSlope(
|
||||||
xValues: number[], xMean: number, yValues: number[], yMean: number) {
|
xValues: number[], xMean: number, yValues: number[], yMean: number) {
|
||||||
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
||||||
var dividendSum = 0;
|
let dividendSum = 0;
|
||||||
var divisorSum = 0;
|
let divisorSum = 0;
|
||||||
for (var i = 0; i < xValues.length; i++) {
|
for (let i = 0; i < xValues.length; i++) {
|
||||||
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
||||||
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ export type PerfLogEvent = {
|
|||||||
*/
|
*/
|
||||||
export abstract class WebDriverExtension {
|
export abstract class WebDriverExtension {
|
||||||
static provideFirstSupported(childTokens: any[]): any[] {
|
static provideFirstSupported(childTokens: any[]): any[] {
|
||||||
var res = [
|
const res = [
|
||||||
{
|
{
|
||||||
provide: _CHILDREN,
|
provide: _CHILDREN,
|
||||||
useFactory: (injector: Injector) => childTokens.map(token => injector.get(token)),
|
useFactory: (injector: Injector) => childTokens.map(token => injector.get(token)),
|
||||||
@ -43,7 +43,7 @@ export abstract class WebDriverExtension {
|
|||||||
{
|
{
|
||||||
provide: WebDriverExtension,
|
provide: WebDriverExtension,
|
||||||
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
||||||
var delegate: WebDriverExtension;
|
let delegate: WebDriverExtension;
|
||||||
children.forEach(extension => {
|
children.forEach(extension => {
|
||||||
if (extension.supports(capabilities)) {
|
if (extension.supports(capabilities)) {
|
||||||
delegate = extension;
|
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) {
|
if (!userAgent) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
let v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||||
if (!v) {
|
if (!v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
var script = `console.timeEnd('${name}');`;
|
let script = `console.timeEnd('${name}');`;
|
||||||
if (restartName) {
|
if (restartName) {
|
||||||
script += `console.time('${restartName}');`;
|
script += `console.time('${restartName}');`;
|
||||||
}
|
}
|
||||||
@ -67,9 +67,9 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
return this._driver.executeScript('1+1')
|
return this._driver.executeScript('1+1')
|
||||||
.then((_) => this._driver.logs('performance'))
|
.then((_) => this._driver.logs('performance'))
|
||||||
.then((entries) => {
|
.then((entries) => {
|
||||||
var events: PerfLogEvent[] = [];
|
const events: PerfLogEvent[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
var message = JSON.parse(entry['message'])['message'];
|
const message = JSON.parse(entry['message'])['message'];
|
||||||
if (message['method'] === 'Tracing.dataCollected') {
|
if (message['method'] === 'Tracing.dataCollected') {
|
||||||
events.push(message['params']);
|
events.push(message['params']);
|
||||||
}
|
}
|
||||||
@ -95,8 +95,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||||
var name = event['name'];
|
const name = event['name'];
|
||||||
var args = event['args'];
|
const args = event['args'];
|
||||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||||
return normalizeEvent(event, {'name': name});
|
return normalizeEvent(event, {'name': name});
|
||||||
} else if (this._isEvent(
|
} else if (this._isEvent(
|
||||||
@ -109,7 +109,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
// new surfaces framework (not broadly enabled yet)
|
// new surfaces framework (not broadly enabled yet)
|
||||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||||
// always available if something is rendered
|
// always available if something is rendered
|
||||||
var frameCount = event['args']['data']['frame_count'];
|
const frameCount = event['args']['data']['frame_count'];
|
||||||
if (frameCount > 1) {
|
if (frameCount > 1) {
|
||||||
throw new Error('multi-frame render stats not supported');
|
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')) {
|
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||||
return normalizeEvent(event, {'name': 'render'});
|
return normalizeEvent(event, {'name': 'render'});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||||
var normArgs = {
|
const normArgs = {
|
||||||
'majorGc': true,
|
'majorGc': true,
|
||||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
};
|
};
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||||
var normArgs = {
|
const normArgs = {
|
||||||
'majorGc': false,
|
'majorGc': false,
|
||||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
@ -151,11 +151,11 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
|
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
|
||||||
return normalizeEvent(event, {'name': 'render'});
|
return normalizeEvent(event, {'name': 'render'});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
|
} 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});
|
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
|
||||||
let data = args['data'];
|
const data = args['data'];
|
||||||
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
const normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||||
return normalizeEvent(event, {'name': 'navigationStart'});
|
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||||
@ -168,7 +168,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
private _isEvent(
|
private _isEvent(
|
||||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||||
expectedName: string = null): boolean {
|
expectedName: string = null): boolean {
|
||||||
var hasCategories = expectedCategories.reduce(
|
const hasCategories = expectedCategories.reduce(
|
||||||
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||||
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||||
}
|
}
|
||||||
@ -183,7 +183,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||||
var ph = chromeEvent['ph'].toUpperCase();
|
let ph = chromeEvent['ph'].toUpperCase();
|
||||||
if (ph === 'S') {
|
if (ph === 'S') {
|
||||||
ph = 'B';
|
ph = 'B';
|
||||||
} else if (ph === 'F') {
|
} else if (ph === 'F') {
|
||||||
@ -192,16 +192,16 @@ function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent):
|
|||||||
// mark events from navigation timing
|
// mark events from navigation timing
|
||||||
ph = 'I';
|
ph = 'I';
|
||||||
}
|
}
|
||||||
var result: {[key: string]: any} =
|
const result: {[key: string]: any} =
|
||||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||||
if (ph === 'X') {
|
if (ph === 'X') {
|
||||||
var dur = chromeEvent['dur'];
|
let dur = chromeEvent['dur'];
|
||||||
if (dur === undefined) {
|
if (dur === undefined) {
|
||||||
dur = chromeEvent['tdur'];
|
dur = chromeEvent['tdur'];
|
||||||
}
|
}
|
||||||
result['dur'] = !dur ? 0.0 : dur / 1000;
|
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||||
}
|
}
|
||||||
for (let prop in data) {
|
for (const prop in data) {
|
||||||
result[prop] = data[prop];
|
result[prop] = data[prop];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -34,7 +34,7 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
var script = 'window.markEnd("' + name + '");';
|
let script = 'window.markEnd("' + name + '");';
|
||||||
if (isPresent(restartName)) {
|
if (isPresent(restartName)) {
|
||||||
script += 'window.markStart("' + restartName + '");';
|
script += 'window.markStart("' + restartName + '");';
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
var script = `console.timeEnd('${name}');`;
|
let script = `console.timeEnd('${name}');`;
|
||||||
if (isPresent(restartName)) {
|
if (isPresent(restartName)) {
|
||||||
script += `console.time('${restartName}');`;
|
script += `console.time('${restartName}');`;
|
||||||
}
|
}
|
||||||
@ -39,9 +39,9 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
return this._driver.executeScript('1+1')
|
return this._driver.executeScript('1+1')
|
||||||
.then((_) => this._driver.logs('performance'))
|
.then((_) => this._driver.logs('performance'))
|
||||||
.then((entries) => {
|
.then((entries) => {
|
||||||
var records: any[] = [];
|
const records: any[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
var message = JSON.parse(entry['message'])['message'];
|
const message = JSON.parse(entry['message'])['message'];
|
||||||
if (message['method'] === 'Timeline.eventRecorded') {
|
if (message['method'] === 'Timeline.eventRecorded') {
|
||||||
records.push(message['params']['record']);
|
records.push(message['params']['record']);
|
||||||
}
|
}
|
||||||
@ -56,11 +56,11 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
events = [];
|
events = [];
|
||||||
}
|
}
|
||||||
records.forEach((record) => {
|
records.forEach((record) => {
|
||||||
var endEvent: PerfLogEvent = null;
|
let endEvent: PerfLogEvent = null;
|
||||||
var type = record['type'];
|
const type = record['type'];
|
||||||
var data = record['data'];
|
const data = record['data'];
|
||||||
var startTime = record['startTime'];
|
const startTime = record['startTime'];
|
||||||
var endTime = record['endTime'];
|
const endTime = record['endTime'];
|
||||||
|
|
||||||
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
|
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
|
||||||
events.push(createStartEvent('script', startTime));
|
events.push(createStartEvent('script', startTime));
|
||||||
@ -95,7 +95,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
function createEvent(
|
function createEvent(
|
||||||
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||||
var result: PerfLogEvent = {
|
const result: PerfLogEvent = {
|
||||||
'cat': 'timeline',
|
'cat': 'timeline',
|
||||||
'name': name,
|
'name': name,
|
||||||
'ts': time,
|
'ts': time,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
require('core-js');
|
require('core-js');
|
||||||
require('reflect-metadata');
|
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 = {
|
exports.config = {
|
||||||
specs: ['spec.js', 'sample_benchmark.js'],
|
specs: ['spec.js', 'sample_benchmark.js'],
|
||||||
|
@ -10,10 +10,10 @@ import {convertPerfProfileToEvents} from '../../src/firefox_extension/lib/parser
|
|||||||
|
|
||||||
function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
||||||
expect(actualEvents.length == expectedEvents.length);
|
expect(actualEvents.length == expectedEvents.length);
|
||||||
for (var i = 0; i < actualEvents.length; ++i) {
|
for (let i = 0; i < actualEvents.length; ++i) {
|
||||||
var actualEvent = actualEvents[i];
|
const actualEvent = actualEvents[i];
|
||||||
var expectedEvent = expectedEvents[i];
|
const expectedEvent = expectedEvents[i];
|
||||||
for (var key in actualEvent) {
|
for (const key in actualEvent) {
|
||||||
expect(actualEvent[key]).toEqual(expectedEvent[key]);
|
expect(actualEvent[key]).toEqual(expectedEvent[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,17 +22,17 @@ function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
|||||||
export function main() {
|
export function main() {
|
||||||
describe('convertPerfProfileToEvents', function() {
|
describe('convertPerfProfileToEvents', function() {
|
||||||
it('should convert single instantaneous event', function() {
|
it('should convert single instantaneous event', function() {
|
||||||
var profileData = {
|
const profileData = {
|
||||||
threads: [
|
threads: [
|
||||||
{samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]}
|
{samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert single non-instantaneous event', function() {
|
it('should convert single non-instantaneous event', function() {
|
||||||
var profileData = {
|
const profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -41,13 +41,13 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(
|
assertEventsEqual(
|
||||||
perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]);
|
perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert multiple instantaneous events', function() {
|
it('should convert multiple instantaneous events', function() {
|
||||||
var profileData = {
|
const profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -55,13 +55,13 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(
|
assertEventsEqual(
|
||||||
perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]);
|
perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert multiple mixed events', function() {
|
it('should convert multiple mixed events', function() {
|
||||||
var profileData = {
|
const profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -71,7 +71,7 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [
|
assertEventsEqual(perfEvents, [
|
||||||
{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'},
|
{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'},
|
||||||
{ph: 'B', ts: 5, name: 'script'}, {ph: 'E', ts: 10, name: 'script'}
|
{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() {
|
it('should add args to gc events', function() {
|
||||||
var profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
const profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
||||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]);
|
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip unknown events', function() {
|
it('should skip unknown events', function() {
|
||||||
var profileData = {
|
const profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{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'}]);
|
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var benchpress = require('../../index.js');
|
import {$, browser} from 'protractor';
|
||||||
var runner = new benchpress.Runner([
|
|
||||||
|
const benchpress = require('../../index.js');
|
||||||
|
const runner = new benchpress.Runner([
|
||||||
// use protractor as Webdriver client
|
// use protractor as Webdriver client
|
||||||
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||||
// use RegressionSlopeValidator to validate samples
|
// use RegressionSlopeValidator to validate samples
|
||||||
|
@ -6,9 +6,12 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var assertEventsContainsName = function(events: any[], eventName: string) {
|
/* tslint:disable:no-console */
|
||||||
var found = false;
|
import {browser} from 'protractor';
|
||||||
for (var i = 0; i < events.length; ++i) {
|
|
||||||
|
const assertEventsContainsName = function(events: any[], eventName: string) {
|
||||||
|
let found = false;
|
||||||
|
for (let i = 0; i < events.length; ++i) {
|
||||||
if (events[i].name == eventName) {
|
if (events[i].name == eventName) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@ -18,7 +21,7 @@ var assertEventsContainsName = function(events: any[], eventName: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('firefox extension', function() {
|
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() {
|
it('should measure performance', function() {
|
||||||
browser.sleep(3000); // wait for extension to load
|
browser.sleep(3000); // wait for extension to load
|
||||||
|
@ -11,12 +11,12 @@ import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createMetric(ids: any[]) {
|
function createMetric(ids: any[]) {
|
||||||
var m = ReflectiveInjector
|
const m = ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||||
MultiMetric.provideWith(ids)
|
MultiMetric.provideWith(ids)
|
||||||
])
|
])
|
||||||
.get(MultiMetric);
|
.get(MultiMetric);
|
||||||
return Promise.resolve(m);
|
return Promise.resolve(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,13 +56,13 @@ class MockMetric extends Metric {
|
|||||||
beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); }
|
beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); }
|
||||||
|
|
||||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||||
var result: {[key: string]: any} = {};
|
const result: {[key: string]: any} = {};
|
||||||
result[this._id] = {'restart': restart};
|
result[this._id] = {'restart': restart};
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe(): {[key: string]: string} {
|
describe(): {[key: string]: string} {
|
||||||
var result: {[key: string]: string} = {};
|
const result: {[key: string]: string} = {};
|
||||||
result[this._id] = 'describe';
|
result[this._id] = 'describe';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ import {isPresent} from '../../src/facade/lang';
|
|||||||
import {TraceEventFactory} from '../trace_event_factory';
|
import {TraceEventFactory} from '../trace_event_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var commandLog: any[];
|
let commandLog: any[];
|
||||||
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
const eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createMetric(
|
function createMetric(
|
||||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||||
@ -34,7 +34,7 @@ export function main() {
|
|||||||
if (!microMetrics) {
|
if (!microMetrics) {
|
||||||
microMetrics = {};
|
microMetrics = {};
|
||||||
}
|
}
|
||||||
var providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||||
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
||||||
provide: PerflogMetric.SET_TIMEOUT,
|
provide: PerflogMetric.SET_TIMEOUT,
|
||||||
@ -66,7 +66,7 @@ export function main() {
|
|||||||
describe('perflog metric', () => {
|
describe('perflog metric', () => {
|
||||||
|
|
||||||
function sortedKeys(stringMap: {[key: string]: any}) {
|
function sortedKeys(stringMap: {[key: string]: any}) {
|
||||||
var res: string[] = [];
|
const res: string[] = [];
|
||||||
res.push(...Object.keys(stringMap));
|
res.push(...Object.keys(stringMap));
|
||||||
res.sort();
|
res.sort();
|
||||||
return res;
|
return res;
|
||||||
@ -102,15 +102,15 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should describe itself based on micro metrics', () => {
|
it('should describe itself based on micro metrics', () => {
|
||||||
var description =
|
const description =
|
||||||
createMetric([[]], null, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe();
|
createMetric([[]], null, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe();
|
||||||
expect(description['myMicroMetric']).toEqual('someDesc');
|
expect(description['myMicroMetric']).toEqual('someDesc');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should describe itself if frame capture is requested and available', () => {
|
it('should describe itself if frame capture is requested and available', () => {
|
||||||
var description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
const description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
||||||
captureFrames: true
|
captureFrames: true
|
||||||
}).describe();
|
}).describe();
|
||||||
expect(description['frameTime.mean']).not.toContain('WARNING');
|
expect(description['frameTime.mean']).not.toContain('WARNING');
|
||||||
expect(description['frameTime.best']).not.toContain('WARNING');
|
expect(description['frameTime.best']).not.toContain('WARNING');
|
||||||
expect(description['frameTime.worst']).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', () => {
|
it('should describe itself if frame capture is requested and not available', () => {
|
||||||
var description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
const description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
||||||
captureFrames: true
|
captureFrames: true
|
||||||
}).describe();
|
}).describe();
|
||||||
expect(description['frameTime.mean']).toContain('WARNING');
|
expect(description['frameTime.mean']).toContain('WARNING');
|
||||||
expect(description['frameTime.best']).toContain('WARNING');
|
expect(description['frameTime.best']).toContain('WARNING');
|
||||||
expect(description['frameTime.worst']).toContain('WARNING');
|
expect(description['frameTime.worst']).toContain('WARNING');
|
||||||
@ -131,7 +131,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should not force gc and mark the timeline',
|
it('should not force gc and mark the timeline',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var metric = createMetric([[]], null);
|
const metric = createMetric([[]], null);
|
||||||
metric.beginMeasure().then((_) => {
|
metric.beginMeasure().then((_) => {
|
||||||
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should force gc and mark the timeline',
|
it('should force gc and mark the timeline',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var metric = createMetric([[]], null, {forceGc: true});
|
const metric = createMetric([[]], null, {forceGc: true});
|
||||||
metric.beginMeasure().then((_) => {
|
metric.beginMeasure().then((_) => {
|
||||||
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
||||||
|
|
||||||
@ -155,11 +155,11 @@ export function main() {
|
|||||||
|
|
||||||
it('should mark and aggregate events in between the marks',
|
it('should mark and aggregate events in between the marks',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [[
|
const events = [[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10)
|
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) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(commandLog).toEqual([
|
expect(commandLog).toEqual([
|
||||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog'
|
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog'
|
||||||
@ -172,13 +172,13 @@ export function main() {
|
|||||||
|
|
||||||
it('should mark and aggregate events since navigationStart',
|
it('should mark and aggregate events since navigationStart',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [[
|
const events = [[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
||||||
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
||||||
eventFactory.markEnd('benchpress0', 10)
|
eventFactory.markEnd('benchpress0', 10)
|
||||||
]];
|
]];
|
||||||
var metric = createMetric(events, null);
|
const metric = createMetric(events, null);
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(data['scriptTime']).toBe(1);
|
expect(data['scriptTime']).toBe(1);
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [
|
const events = [
|
||||||
[
|
[
|
||||||
eventFactory.markStart('benchpress0', 0),
|
eventFactory.markStart('benchpress0', 0),
|
||||||
eventFactory.markEnd('benchpress0', 1),
|
eventFactory.markEnd('benchpress0', 1),
|
||||||
@ -195,7 +195,7 @@ export function main() {
|
|||||||
],
|
],
|
||||||
[eventFactory.markEnd('benchpress1', 3)]
|
[eventFactory.markEnd('benchpress1', 3)]
|
||||||
];
|
];
|
||||||
var metric = createMetric(events, null);
|
const metric = createMetric(events, null);
|
||||||
metric.beginMeasure()
|
metric.beginMeasure()
|
||||||
.then((_) => metric.endMeasure(true))
|
.then((_) => metric.endMeasure(true))
|
||||||
.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',
|
it('should loop and aggregate until the end mark is present',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [
|
const events = [
|
||||||
[eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)],
|
[eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)],
|
||||||
[eventFactory.end('script', 2)],
|
[eventFactory.end('script', 2)],
|
||||||
[
|
[
|
||||||
@ -219,7 +219,7 @@ export function main() {
|
|||||||
eventFactory.markEnd('benchpress0', 10)
|
eventFactory.markEnd('benchpress0', 10)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
var metric = createMetric(events, null);
|
const metric = createMetric(events, null);
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(commandLog).toEqual([
|
expect(commandLog).toEqual([
|
||||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog',
|
['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',
|
it('should store events after the end mark for the next call',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [
|
const events = [
|
||||||
[
|
[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1),
|
eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1),
|
||||||
eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1),
|
eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1),
|
||||||
@ -244,7 +244,7 @@ export function main() {
|
|||||||
eventFactory.markEnd('benchpress1', 6)
|
eventFactory.markEnd('benchpress1', 6)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
var metric = createMetric(events, null);
|
const metric = createMetric(events, null);
|
||||||
metric.beginMeasure()
|
metric.beginMeasure()
|
||||||
.then((_) => metric.endMeasure(true))
|
.then((_) => metric.endMeasure(true))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
@ -263,7 +263,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe('with forced gc', () => {
|
describe('with forced gc', () => {
|
||||||
var events: PerfLogEvent[][];
|
let events: PerfLogEvent[][];
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
events = [[
|
events = [[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
@ -276,7 +276,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
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) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(commandLog).toEqual([
|
expect(commandLog).toEqual([
|
||||||
['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'],
|
['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'],
|
||||||
@ -291,7 +291,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should restart after the forced gc if needed',
|
it('should restart after the forced gc if needed',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
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) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => {
|
||||||
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ export function main() {
|
|||||||
} = {}) {
|
} = {}) {
|
||||||
events.unshift(eventFactory.markStart('benchpress0', 0));
|
events.unshift(eventFactory.markStart('benchpress0', 0));
|
||||||
events.push(eventFactory.markEnd('benchpress0', 10));
|
events.push(eventFactory.markEnd('benchpress0', 10));
|
||||||
var metric = createMetric([events], null, {
|
const metric = createMetric([events], null, {
|
||||||
microMetrics: microMetrics,
|
microMetrics: microMetrics,
|
||||||
captureFrames: captureFrames,
|
captureFrames: captureFrames,
|
||||||
receivedData: receivedData,
|
receivedData: receivedData,
|
||||||
@ -502,8 +502,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should ignore events from different processed as the start mark',
|
it('should ignore events from different processed as the start mark',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||||
var metric = createMetric(
|
const metric = createMetric(
|
||||||
[[
|
[[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null),
|
||||||
eventFactory.end('script', 5, null),
|
eventFactory.end('script', 5, null),
|
||||||
@ -685,7 +685,7 @@ class MockDriverExtension extends WebDriverExtension {
|
|||||||
readPerfLog(): Promise<any> {
|
readPerfLog(): Promise<any> {
|
||||||
this._commandLog.push('readPerfLog');
|
this._commandLog.push('readPerfLog');
|
||||||
if (this._perfLogs.length > 0) {
|
if (this._perfLogs.length > 0) {
|
||||||
var next = this._perfLogs[0];
|
const next = this._perfLogs[0];
|
||||||
this._perfLogs.shift();
|
this._perfLogs.shift();
|
||||||
return Promise.resolve(next);
|
return Promise.resolve(next);
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,7 +12,7 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te
|
|||||||
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var wdAdapter: MockDriverAdapter;
|
let wdAdapter: MockDriverAdapter;
|
||||||
|
|
||||||
function createMetric(
|
function createMetric(
|
||||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
userMetrics = {};
|
userMetrics = {};
|
||||||
}
|
}
|
||||||
wdAdapter = new MockDriverAdapter();
|
wdAdapter = new MockDriverAdapter();
|
||||||
var providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
||||||
{provide: Options.USER_METRICS, useValue: userMetrics},
|
{provide: Options.USER_METRICS, useValue: userMetrics},
|
||||||
{provide: WebDriverAdapter, useValue: wdAdapter}
|
{provide: WebDriverAdapter, useValue: wdAdapter}
|
||||||
@ -45,7 +45,7 @@ export function main() {
|
|||||||
describe('endMeasure', () => {
|
describe('endMeasure', () => {
|
||||||
it('should stop measuring when all properties have numeric values',
|
it('should stop measuring when all properties have numeric values',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
let metric = createMetric(
|
const metric = createMetric(
|
||||||
[[]], new PerfLogFeatures(),
|
[[]], new PerfLogFeatures(),
|
||||||
{userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}});
|
{userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}});
|
||||||
metric.beginMeasure()
|
metric.beginMeasure()
|
||||||
@ -71,7 +71,7 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
executeScript(script: string): any {
|
executeScript(script: string): any {
|
||||||
// Just handles `return window.propName` ignores `delete window.propName`.
|
// Just handles `return window.propName` ignores `delete window.propName`.
|
||||||
if (script.indexOf('return window.') == 0) {
|
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]);
|
return Promise.resolve(this.data[metricName]);
|
||||||
} else if (script.indexOf('delete window.') == 0) {
|
} else if (script.indexOf('delete window.') == 0) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
|
@ -14,8 +14,8 @@ import {isBlank, isPresent} from '../../src/facade/lang';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('console reporter', () => {
|
describe('console reporter', () => {
|
||||||
var reporter: ConsoleReporter;
|
let reporter: ConsoleReporter;
|
||||||
var log: string[];
|
let log: string[];
|
||||||
|
|
||||||
function createReporter(
|
function createReporter(
|
||||||
{columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
|
{columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
|
||||||
@ -31,7 +31,7 @@ export function main() {
|
|||||||
if (sampleId == null) {
|
if (sampleId == null) {
|
||||||
sampleId = 'null';
|
sampleId = 'null';
|
||||||
}
|
}
|
||||||
var providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
ConsoleReporter.PROVIDERS, {
|
ConsoleReporter.PROVIDERS, {
|
||||||
provide: SampleDescription,
|
provide: SampleDescription,
|
||||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||||
|
@ -13,7 +13,7 @@ import {isPresent} from '../../src/facade/lang';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('file reporter', () => {
|
describe('file reporter', () => {
|
||||||
var loggedFile: any;
|
let loggedFile: any;
|
||||||
|
|
||||||
function createReporter({sampleId, descriptions, metrics, path}: {
|
function createReporter({sampleId, descriptions, metrics, path}: {
|
||||||
sampleId: string,
|
sampleId: string,
|
||||||
@ -21,7 +21,7 @@ export function main() {
|
|||||||
metrics: {[key: string]: string},
|
metrics: {[key: string]: string},
|
||||||
path: string
|
path: string
|
||||||
}) {
|
}) {
|
||||||
var providers = [
|
const providers = [
|
||||||
JsonFileReporter.PROVIDERS, {
|
JsonFileReporter.PROVIDERS, {
|
||||||
provide: SampleDescription,
|
provide: SampleDescription,
|
||||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||||
@ -49,9 +49,9 @@ export function main() {
|
|||||||
.reportSample(
|
.reportSample(
|
||||||
[mv(0, 0, {'a': 3, 'b': 6})],
|
[mv(0, 0, {'a': 3, 'b': 6})],
|
||||||
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
[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);
|
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
||||||
var parsedContent = JSON.parse(loggedFile['content']);
|
const parsedContent = JSON.parse(loggedFile['content']);
|
||||||
expect(parsedContent).toEqual({
|
expect(parsedContent).toEqual({
|
||||||
'description': {
|
'description': {
|
||||||
'id': 'someId',
|
'id': 'someId',
|
||||||
|
@ -12,12 +12,12 @@ import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createReporters(ids: any[]) {
|
function createReporters(ids: any[]) {
|
||||||
var r = ReflectiveInjector
|
const r = ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||||
MultiReporter.provideWith(ids)
|
MultiReporter.provideWith(ids)
|
||||||
])
|
])
|
||||||
.get(MultiReporter);
|
.get(MultiReporter);
|
||||||
return Promise.resolve(r);
|
return Promise.resolve(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should reportMeasureValues to all',
|
it('should reportMeasureValues to all',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
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) => {
|
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
||||||
|
|
||||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
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) => {
|
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var completeSample =
|
const completeSample =
|
||||||
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
||||||
var validSample = [completeSample[1]];
|
const validSample = [completeSample[1]];
|
||||||
|
|
||||||
createReporters(['m1', 'm2'])
|
createReporters(['m1', 'm2'])
|
||||||
.then((r) => r.reportSample(completeSample, validSample))
|
.then((r) => r.reportSample(completeSample, validSample))
|
||||||
|
@ -12,8 +12,8 @@ import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('runner', () => {
|
describe('runner', () => {
|
||||||
var injector: ReflectiveInjector;
|
let injector: ReflectiveInjector;
|
||||||
var runner: Runner;
|
let runner: Runner;
|
||||||
|
|
||||||
function createRunner(defaultProviders: any[] = null): Runner {
|
function createRunner(defaultProviders: any[] = null): Runner {
|
||||||
if (!defaultProviders) {
|
if (!defaultProviders) {
|
||||||
@ -76,7 +76,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should provide Options.EXECUTE',
|
it('should provide Options.EXECUTE',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var execute = () => {};
|
const execute = () => {};
|
||||||
createRunner().sample({id: 'someId', execute: execute}).then((_) => {
|
createRunner().sample({id: 'someId', execute: execute}).then((_) => {
|
||||||
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
||||||
async.done();
|
async.done();
|
||||||
@ -85,7 +85,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should provide Options.PREPARE',
|
it('should provide Options.PREPARE',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var prepare = () => {};
|
const prepare = () => {};
|
||||||
createRunner().sample({id: 'someId', prepare: prepare}).then((_) => {
|
createRunner().sample({id: 'someId', prepare: prepare}).then((_) => {
|
||||||
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -12,10 +12,10 @@ import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, V
|
|||||||
import {isBlank, isPresent} from '../src/facade/lang';
|
import {isBlank, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var EMPTY_EXECUTE = () => {};
|
const EMPTY_EXECUTE = () => {};
|
||||||
|
|
||||||
describe('sampler', () => {
|
describe('sampler', () => {
|
||||||
var sampler: Sampler;
|
let sampler: Sampler;
|
||||||
|
|
||||||
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
||||||
driver?: any,
|
driver?: any,
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
prepare?: any,
|
prepare?: any,
|
||||||
execute?: any
|
execute?: any
|
||||||
} = {}) {
|
} = {}) {
|
||||||
var time = 1000;
|
let time = 1000;
|
||||||
if (!metric) {
|
if (!metric) {
|
||||||
metric = new MockMetric([]);
|
metric = new MockMetric([]);
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ export function main() {
|
|||||||
if (isBlank(driver)) {
|
if (isBlank(driver)) {
|
||||||
driver = new MockDriverAdapter([]);
|
driver = new MockDriverAdapter([]);
|
||||||
}
|
}
|
||||||
var providers = [
|
const providers = [
|
||||||
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
||||||
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
||||||
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
{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',
|
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var log: any[] = [];
|
const log: any[] = [];
|
||||||
var count = 0;
|
let count = 0;
|
||||||
var driver = new MockDriverAdapter([], (callback: Function) => {
|
const driver = new MockDriverAdapter([], (callback: Function) => {
|
||||||
var result = callback();
|
const result = callback();
|
||||||
log.push(result);
|
log.push(result);
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
});
|
});
|
||||||
@ -73,8 +73,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var workCount = 0;
|
let workCount = 0;
|
||||||
var log: any[] = [];
|
const log: any[] = [];
|
||||||
createSampler({
|
createSampler({
|
||||||
metric: createCountingMetric(log),
|
metric: createCountingMetric(log),
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
@ -98,8 +98,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var log: any[] = [];
|
const log: any[] = [];
|
||||||
var workCount = 0;
|
let workCount = 0;
|
||||||
createSampler({
|
createSampler({
|
||||||
metric: createCountingMetric(log),
|
metric: createCountingMetric(log),
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
@ -120,14 +120,14 @@ export function main() {
|
|||||||
|
|
||||||
it('should only collect metrics for execute and ignore metrics from prepare',
|
it('should only collect metrics for execute and ignore metrics from prepare',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var scriptTime = 0;
|
let scriptTime = 0;
|
||||||
var iterationCount = 1;
|
let iterationCount = 1;
|
||||||
createSampler({
|
createSampler({
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
metric: new MockMetric(
|
metric: new MockMetric(
|
||||||
[],
|
[],
|
||||||
() => {
|
() => {
|
||||||
var result = Promise.resolve({'script': scriptTime});
|
const result = Promise.resolve({'script': scriptTime});
|
||||||
scriptTime = 0;
|
scriptTime = 0;
|
||||||
return result;
|
return result;
|
||||||
}),
|
}),
|
||||||
@ -147,8 +147,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should call the validator for every execution and store the valid sample',
|
it('should call the validator for every execution and store the valid sample',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var log: any[] = [];
|
const log: any[] = [];
|
||||||
var validSample = [mv(null, null, {})];
|
const validSample = [mv(null, null, {})];
|
||||||
|
|
||||||
createSampler({
|
createSampler({
|
||||||
metric: createCountingMetric(),
|
metric: createCountingMetric(),
|
||||||
@ -174,8 +174,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should report the metric values',
|
it('should report the metric values',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var log: any[] = [];
|
const log: any[] = [];
|
||||||
var validSample = [mv(null, null, {})];
|
const validSample = [mv(null, null, {})];
|
||||||
createSampler({
|
createSampler({
|
||||||
validator: createCountingValidator(2, validSample),
|
validator: createCountingValidator(2, validSample),
|
||||||
metric: createCountingMetric(),
|
metric: createCountingMetric(),
|
||||||
@ -220,7 +220,7 @@ function createCountingValidator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createCountingMetric(log: any[] = []) {
|
function createCountingMetric(log: any[] = []) {
|
||||||
var scriptTime = 0;
|
let scriptTime = 0;
|
||||||
return new MockMetric(log, () => ({'script': scriptTime++}));
|
return new MockMetric(log, () => ({'script': scriptTime++}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +239,8 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
class MockValidator extends Validator {
|
class MockValidator extends Validator {
|
||||||
constructor(private _log: any[] = [], private _validate: Function = null) { super(); }
|
constructor(private _log: any[] = [], private _validate: Function = null) { super(); }
|
||||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
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]);
|
this._log.push(['validate', completeSample, stableSample]);
|
||||||
return stableSample;
|
return stableSample;
|
||||||
}
|
}
|
||||||
@ -252,7 +253,7 @@ class MockMetric extends Metric {
|
|||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
endMeasure(restart: boolean) {
|
endMeasure(restart: boolean) {
|
||||||
var measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
const measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||||
this._log.push(['endMeasure', restart, measureValues]);
|
this._log.push(['endMeasure', restart, measureValues]);
|
||||||
return Promise.resolve(measureValues);
|
return Promise.resolve(measureValues);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export class TraceEventFactory {
|
|||||||
constructor(private _cat: string, private _pid: string) {}
|
constructor(private _cat: string, private _pid: string) {}
|
||||||
|
|
||||||
create(ph: any, name: string, time: number, args: any = null) {
|
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};
|
PerfLogEvent = {'name': name, 'cat': this._cat, 'ph': ph, 'ts': time, 'pid': this._pid};
|
||||||
if (isPresent(args)) {
|
if (isPresent(args)) {
|
||||||
res['args'] = args;
|
res['args'] = args;
|
||||||
@ -34,7 +34,7 @@ export class TraceEventFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
complete(name: string, time: number, duration: number, args: any = null) {
|
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;
|
res['dur'] = duration;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../..
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('regression slope validator', () => {
|
describe('regression slope validator', () => {
|
||||||
var validator: RegressionSlopeValidator;
|
let validator: RegressionSlopeValidator;
|
||||||
|
|
||||||
function createValidator({size, metric}: {size: number, metric: string}) {
|
function createValidator({size, metric}: {size: number, metric: string}) {
|
||||||
validator = ReflectiveInjector
|
validator = ReflectiveInjector
|
||||||
@ -42,14 +42,14 @@ export function main() {
|
|||||||
|
|
||||||
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
||||||
createValidator({size: 2, metric: 'script'});
|
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.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
||||||
createValidator({size: 2, metric: 'script'});
|
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.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||||
});
|
});
|
||||||
|
@ -12,7 +12,7 @@ import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('size validator', () => {
|
describe('size validator', () => {
|
||||||
var validator: SizeValidator;
|
let validator: SizeValidator;
|
||||||
|
|
||||||
function createValidator(size: number) {
|
function createValidator(size: number) {
|
||||||
validator =
|
validator =
|
||||||
@ -35,7 +35,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should return the last sampleSize runs when it has at least the given size', () => {
|
it('should return the last sampleSize runs when it has at least the given size', () => {
|
||||||
createValidator(2);
|
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.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||||
});
|
});
|
||||||
|
@ -14,23 +14,23 @@ import {TraceEventFactory} from '../trace_event_factory';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('chrome driver extension', () => {
|
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"';
|
'"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[];
|
let log: any[];
|
||||||
var extension: ChromeDriverExtension;
|
let extension: ChromeDriverExtension;
|
||||||
|
|
||||||
var blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
const blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||||
var v8Events = new TraceEventFactory('v8', 'pid0');
|
const v8Events = new TraceEventFactory('v8', 'pid0');
|
||||||
var v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
const v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||||
var chromeTimelineEvents =
|
const chromeTimelineEvents =
|
||||||
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||||
var chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
const chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
||||||
var chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
const chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
||||||
var chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
const chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
||||||
var chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
const chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
||||||
var benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
const benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
||||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createExtension(
|
function createExtension(
|
||||||
perfRecords: any[] = null, userAgent: string = null,
|
perfRecords: any[] = null, userAgent: string = null,
|
||||||
@ -101,7 +101,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should normalize "tdur" to "dur"',
|
it('should normalize "tdur" to "dur"',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
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;
|
event['tdur'] = 5500;
|
||||||
createExtension([event]).readPerfLog().then((events) => {
|
createExtension([event]).readPerfLog().then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
|
@ -13,10 +13,10 @@ import {TraceEventFactory} from '../trace_event_factory';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ios driver extension', () => {
|
describe('ios driver extension', () => {
|
||||||
var log: any[];
|
let log: any[];
|
||||||
var extension: IOsDriverExtension;
|
let extension: IOsDriverExtension;
|
||||||
|
|
||||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
||||||
if (!perfRecords) {
|
if (!perfRecords) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
import {isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent, stringify} from '../facade/lang';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
@ -108,8 +108,14 @@ export class NgClass implements DoCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _applyIterableChanges(changes: any): void {
|
private _applyIterableChanges(changes: any): void {
|
||||||
changes.forEachAddedItem(
|
changes.forEachAddedItem((record: CollectionChangeRecord) => {
|
||||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, true));
|
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(
|
changes.forEachRemovedItem(
|
||||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
(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++) {
|
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.index = i;
|
||||||
viewRef.context.count = ilen;
|
viewRef.context.count = ilen;
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.forEachIdentityChange((record: any) => {
|
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;
|
viewRef.context.$implicit = record.item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
|||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* The styles are updated according to the value of the expression evaluation:
|
* 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).
|
* - values are the values assigned to those properties (expressed in the given unit).
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
|
@ -6,19 +6,31 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, DoCheck, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
|
|
||||||
const _CASE_DEFAULT = {};
|
|
||||||
|
|
||||||
export class SwitchView {
|
export class SwitchView {
|
||||||
|
private _created = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
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]'})
|
@Directive({selector: '[ngSwitch]'})
|
||||||
export class NgSwitch {
|
export class NgSwitch {
|
||||||
private _switchValue: any;
|
private _defaultViews: SwitchView[];
|
||||||
private _useDefault: boolean = false;
|
private _defaultUsed = false;
|
||||||
private _valueViews = new Map<any, SwitchView[]>();
|
private _caseCount = 0;
|
||||||
private _activeViews: SwitchView[] = [];
|
private _lastCaseCheckIndex = 0;
|
||||||
|
private _lastCasesMatched = false;
|
||||||
|
private _ngSwitch: any;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngSwitch(value: any) {
|
set ngSwitch(newValue: any) {
|
||||||
// Set of views to display for this value
|
this._ngSwitch = newValue;
|
||||||
let views = this._valueViews.get(value);
|
if (this._caseCount === 0) {
|
||||||
|
this._updateDefaultCases(true);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_registerView(value: any, view: SwitchView): void {
|
_addCase(): number { return this._caseCount++; }
|
||||||
let views = this._valueViews.get(value);
|
|
||||||
if (!views) {
|
/** @internal */
|
||||||
views = [];
|
_addDefault(view: SwitchView) {
|
||||||
this._valueViews.set(value, views);
|
if (!this._defaultViews) {
|
||||||
|
this._defaultViews = [];
|
||||||
}
|
}
|
||||||
views.push(view);
|
this._defaultViews.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deregisterView(value: any, view: SwitchView): void {
|
/** @internal */
|
||||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
_matchCase(value: any): boolean {
|
||||||
if (value === _CASE_DEFAULT) return;
|
const matched = value == this._ngSwitch;
|
||||||
const views = this._valueViews.get(value);
|
this._lastCasesMatched = this._lastCasesMatched || matched;
|
||||||
if (views.length == 1) {
|
this._lastCaseCheckIndex++;
|
||||||
this._valueViews.delete(value);
|
if (this._lastCaseCheckIndex === this._caseCount) {
|
||||||
} else {
|
this._updateDefaultCases(!this._lastCasesMatched);
|
||||||
ListWrapper.remove(views, view);
|
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
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngSwitchCase]'})
|
@Directive({selector: '[ngSwitchCase]'})
|
||||||
export class NgSwitchCase {
|
export class NgSwitchCase implements DoCheck {
|
||||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
|
||||||
private _value: any = _CASE_DEFAULT;
|
|
||||||
private _view: SwitchView;
|
private _view: SwitchView;
|
||||||
private _switch: NgSwitch;
|
|
||||||
|
@Input()
|
||||||
|
ngSwitchCase: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||||
@Host() ngSwitch: NgSwitch) {
|
@Host() private ngSwitch: NgSwitch) {
|
||||||
this._switch = ngSwitch;
|
ngSwitch._addCase();
|
||||||
this._view = new SwitchView(viewContainer, templateRef);
|
this._view = new SwitchView(viewContainer, templateRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input()
|
ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); }
|
||||||
set ngSwitchCase(value: any) {
|
|
||||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
|
||||||
this._value = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,7 +194,7 @@ export class NgSwitchCase {
|
|||||||
export class NgSwitchDefault {
|
export class NgSwitchDefault {
|
||||||
constructor(
|
constructor(
|
||||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||||
@Host() sswitch: NgSwitch) {
|
@Host() ngSwitch: NgSwitch) {
|
||||||
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
|
ngSwitch._addDefault(new SwitchView(viewContainer, templateRef));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,19 +64,19 @@ export class HashLocationStrategy extends LocationStrategy {
|
|||||||
path(includeHash: boolean = false): string {
|
path(includeHash: boolean = false): string {
|
||||||
// the hash value is always prefixed with a `#`
|
// the hash value is always prefixed with a `#`
|
||||||
// and if it is empty then it will stay empty
|
// and if it is empty then it will stay empty
|
||||||
var path = this._platformLocation.hash;
|
let path = this._platformLocation.hash;
|
||||||
if (!isPresent(path)) path = '#';
|
if (!isPresent(path)) path = '#';
|
||||||
|
|
||||||
return path.length > 0 ? path.substring(1) : path;
|
return path.length > 0 ? path.substring(1) : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareExternalUrl(internal: string): string {
|
prepareExternalUrl(internal: string): string {
|
||||||
var url = Location.joinWithSlash(this._baseHref, internal);
|
const url = Location.joinWithSlash(this._baseHref, internal);
|
||||||
return url.length > 0 ? ('#' + url) : url;
|
return url.length > 0 ? ('#' + url) : url;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
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) {
|
if (url.length == 0) {
|
||||||
url = this._platformLocation.pathname;
|
url = this._platformLocation.pathname;
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ export class HashLocationStrategy extends LocationStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
replaceState(state: any, title: string, path: string, queryParams: string) {
|
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) {
|
if (url.length == 0) {
|
||||||
url = this._platformLocation.pathname;
|
url = this._platformLocation.pathname;
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ export class Location {
|
|||||||
if (end.length == 0) {
|
if (end.length == 0) {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
var slashes = 0;
|
let slashes = 0;
|
||||||
if (start.endsWith('/')) {
|
if (start.endsWith('/')) {
|
||||||
slashes++;
|
slashes++;
|
||||||
}
|
}
|
||||||
|
@ -79,12 +79,12 @@ export class PathLocationStrategy extends LocationStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
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);
|
this._platformLocation.pushState(state, title, externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceState(state: any, title: string, url: string, queryParams: string) {
|
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);
|
this._platformLocation.replaceState(state, title, externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
|
|||||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
||||||
*
|
*
|
||||||
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
||||||
* to the view. The Observable continuesly updates the view with the current time.
|
* to the view. The Observable continuously updates the view with the current time.
|
||||||
*
|
*
|
||||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
import {DateFormatter} from '../facade/intl';
|
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';
|
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.
|
* 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:
|
* WARNINGS:
|
||||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
* - 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
|
* 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) {}
|
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||||
|
|
||||||
transform(value: any, pattern: string = 'mediumDate'): string {
|
transform(value: any, pattern: string = 'mediumDate'): string {
|
||||||
|
let date: Date;
|
||||||
|
|
||||||
if (isBlank(value)) return null;
|
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);
|
throw new InvalidPipeArgumentError(DatePipe, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NumberWrapper.isNumeric(value)) {
|
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBlank(obj: any): boolean {
|
||||||
|
return obj == null || obj === '';
|
||||||
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank} from '../facade/lang';
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,9 +15,10 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | i18nSelect:mapping`
|
* @howToUse `expression | i18nSelect:mapping`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Where:
|
* Where `mapping` is an object that indicates the text that should be displayed
|
||||||
* - `mapping`: is an object that indicates the text that should be displayed
|
|
||||||
* for different values of the provided `expression`.
|
* 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
|
* ## Example
|
||||||
*
|
*
|
||||||
@ -29,12 +29,20 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
@Pipe({name: 'i18nSelect', pure: true})
|
@Pipe({name: 'i18nSelect', pure: true})
|
||||||
export class I18nSelectPipe implements PipeTransform {
|
export class I18nSelectPipe implements PipeTransform {
|
||||||
transform(value: string, mapping: {[key: string]: string}): string {
|
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);
|
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`
|
* @howToUse `expression | lowercase`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
@ -37,7 +37,7 @@ function formatNumber(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (digits) {
|
if (digits) {
|
||||||
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
const parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||||
if (parts === null) {
|
if (parts === null) {
|
||||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | uppercase`
|
* @howToUse `expression | uppercase`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('binding to CSS class list', () => {
|
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(() => {
|
it('should add and remove classes based on changes to the expression object', async(() => {
|
||||||
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
let objExpr = getComponent().objExpr;
|
const objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
detectChangesAndExpectClassName('foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
@ -135,7 +134,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should add and remove classes based on changes to the expression', async(() => {
|
it('should add and remove classes based on changes to the expression', async(() => {
|
||||||
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||||
let arrExpr = getComponent().arrExpr;
|
const arrExpr = getComponent().arrExpr;
|
||||||
detectChangesAndExpectClassName('foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
arrExpr.push('bar');
|
arrExpr.push('bar');
|
||||||
@ -187,6 +186,13 @@ export function main() {
|
|||||||
getComponent().arrExpr = ['foo bar baz foobar'];
|
getComponent().arrExpr = ['foo bar baz foobar'];
|
||||||
detectChangesAndExpectClassName('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', () => {
|
describe('expressions evaluating to sets', () => {
|
||||||
@ -253,7 +259,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should co-operate with the class attribute', async(() => {
|
it('should co-operate with the class attribute', async(() => {
|
||||||
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
||||||
let objExpr = getComponent().objExpr;
|
const objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
objExpr['bar'] = true;
|
objExpr['bar'] = true;
|
||||||
detectChangesAndExpectClassName('init foo bar');
|
detectChangesAndExpectClassName('init foo bar');
|
||||||
@ -267,7 +273,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should co-operate with the interpolated class attribute', async(() => {
|
it('should co-operate with the interpolated class attribute', async(() => {
|
||||||
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
||||||
let objExpr = getComponent().objExpr;
|
const objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
objExpr['bar'] = true;
|
objExpr['bar'] = true;
|
||||||
detectChangesAndExpectClassName(`init foo bar`);
|
detectChangesAndExpectClassName(`init foo bar`);
|
||||||
@ -282,7 +288,7 @@ export function main() {
|
|||||||
it('should co-operate with the class attribute and binding to it', async(() => {
|
it('should co-operate with the class attribute and binding to it', async(() => {
|
||||||
fixture =
|
fixture =
|
||||||
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
||||||
let objExpr = getComponent().objExpr;
|
const objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
objExpr['bar'] = true;
|
objExpr['bar'] = true;
|
||||||
detectChangesAndExpectClassName(`init foo bar`);
|
detectChangesAndExpectClassName(`init foo bar`);
|
||||||
@ -298,7 +304,7 @@ export function main() {
|
|||||||
const template =
|
const template =
|
||||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
let objExpr = getComponent().objExpr;
|
const objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
detectChangesAndExpectClassName('init foo baz');
|
detectChangesAndExpectClassName('init foo baz');
|
||||||
|
|
||||||
@ -316,7 +322,7 @@ export function main() {
|
|||||||
async(() => {
|
async(() => {
|
||||||
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
let cmp = getComponent();
|
const cmp = getComponent();
|
||||||
|
|
||||||
detectChangesAndExpectClassName('init foo');
|
detectChangesAndExpectClassName('init foo');
|
||||||
|
|
||||||
@ -348,4 +354,4 @@ class TestComponent {
|
|||||||
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
.createComponent(TestComponent);
|
.createComponent(TestComponent);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use a default template if a custom one is null', async(() => {
|
it('should use a default template if a custom one is null', async(() => {
|
||||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
||||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
||||||
const cutTemplate =
|
const cutTemplate =
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component} from '@angular/core';
|
import {Attribute, Component, Directive} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -32,93 +32,153 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('switch value changes', () => {
|
describe('switch value changes', () => {
|
||||||
it('should switch amongst when values', async(() => {
|
it('should switch amongst when values', () => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
detectChangesAndExpectText('');
|
detectChangesAndExpectText('');
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when a');
|
detectChangesAndExpectText('when a');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when b');
|
detectChangesAndExpectText('when b');
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('should switch amongst when values with fallback to default', async(() => {
|
it('should switch amongst when values with fallback to default', () => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||||
'<li template="ngSwitchDefault">when default</li>' +
|
'<li template="ngSwitchDefault">when default</li>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when a');
|
detectChangesAndExpectText('when a');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
|
|
||||||
getComponent().switchValue = 'c';
|
getComponent().switchValue = 'c';
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
}));
|
});
|
||||||
|
|
||||||
it('should support multiple whens with the same value', async(() => {
|
it('should support multiple whens with the same value', () => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
detectChangesAndExpectText('when default1;when default2;');
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when a1;when a2;');
|
detectChangesAndExpectText('when a1;when a2;');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when b1;when b2;');
|
detectChangesAndExpectText('when b1;when b2;');
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when values changes', () => {
|
describe('when values changes', () => {
|
||||||
it('should switch amongst when values', async(() => {
|
it('should switch amongst when values', () => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
getComponent().when1 = 'a';
|
getComponent().when1 = 'a';
|
||||||
getComponent().when2 = 'b';
|
getComponent().when2 = 'b';
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when 1;');
|
detectChangesAndExpectText('when 1;');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when 2;');
|
detectChangesAndExpectText('when 2;');
|
||||||
|
|
||||||
getComponent().switchValue = 'c';
|
getComponent().switchValue = 'c';
|
||||||
detectChangesAndExpectText('when default;');
|
detectChangesAndExpectText('when default;');
|
||||||
|
|
||||||
getComponent().when1 = 'c';
|
getComponent().when1 = 'c';
|
||||||
detectChangesAndExpectText('when 1;');
|
detectChangesAndExpectText('when 1;');
|
||||||
|
|
||||||
getComponent().when1 = 'd';
|
getComponent().when1 = 'd';
|
||||||
detectChangesAndExpectText('when default;');
|
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('AsyncPipe', () => {
|
||||||
|
|
||||||
describe('Observable', () => {
|
describe('Observable', () => {
|
||||||
var emitter: EventEmitter<any>;
|
let emitter: EventEmitter<any>;
|
||||||
var pipe: AsyncPipe;
|
let pipe: AsyncPipe;
|
||||||
var ref: any;
|
let ref: any;
|
||||||
var message = {};
|
const message = {};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
emitter = new EventEmitter();
|
emitter = new EventEmitter();
|
||||||
@ -62,7 +62,7 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
pipe.transform(emitter);
|
pipe.transform(emitter);
|
||||||
|
|
||||||
var newEmitter = new EventEmitter();
|
const newEmitter = new EventEmitter();
|
||||||
expect(pipe.transform(newEmitter)).toBe(null);
|
expect(pipe.transform(newEmitter)).toBe(null);
|
||||||
emitter.emit(message);
|
emitter.emit(message);
|
||||||
|
|
||||||
@ -104,14 +104,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Promise', () => {
|
describe('Promise', () => {
|
||||||
var message = new Object();
|
const message = new Object();
|
||||||
var pipe: AsyncPipe;
|
let pipe: AsyncPipe;
|
||||||
var resolve: (result: any) => void;
|
let resolve: (result: any) => void;
|
||||||
var reject: (error: any) => void;
|
let reject: (error: any) => void;
|
||||||
var promise: Promise<any>;
|
let promise: Promise<any>;
|
||||||
var ref: SpyChangeDetectorRef;
|
let ref: SpyChangeDetectorRef;
|
||||||
// adds longer timers for passing tests in IE
|
// adds longer timers for passing tests in IE
|
||||||
var timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
const timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
promise = new Promise((res, rej) => {
|
promise = new Promise((res, rej) => {
|
||||||
@ -154,7 +154,7 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
pipe.transform(promise);
|
pipe.transform(promise);
|
||||||
|
|
||||||
var promise = new Promise<any>(() => {});
|
promise = new Promise<any>(() => {});
|
||||||
expect(pipe.transform(promise)).toBe(null);
|
expect(pipe.transform(promise)).toBe(null);
|
||||||
|
|
||||||
// this should not affect the pipe, so it should return WrappedValue
|
// 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',
|
it('should request a change detection check upon receiving a new value',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var markForCheck = ref.spy('markForCheck');
|
const markForCheck = ref.spy('markForCheck');
|
||||||
pipe.transform(promise);
|
pipe.transform(promise);
|
||||||
resolve(message);
|
resolve(message);
|
||||||
|
|
||||||
@ -202,14 +202,14 @@ export function main() {
|
|||||||
|
|
||||||
describe('null', () => {
|
describe('null', () => {
|
||||||
it('should return null when given null', () => {
|
it('should return null when given null', () => {
|
||||||
var pipe = new AsyncPipe(null);
|
const pipe = new AsyncPipe(null);
|
||||||
expect(pipe.transform(null)).toEqual(null);
|
expect(pipe.transform(null)).toEqual(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('other types', () => {
|
describe('other types', () => {
|
||||||
it('should throw when given an invalid object', () => {
|
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();
|
expect(() => pipe.transform(<any>'some bogus object')).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,13 +8,18 @@
|
|||||||
|
|
||||||
import {DatePipe} from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
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';
|
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DatePipe', () => {
|
describe('DatePipe', () => {
|
||||||
var date: Date;
|
let date: Date;
|
||||||
var pipe: DatePipe;
|
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
|
// 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
|
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
||||||
@ -34,7 +39,9 @@ export function main() {
|
|||||||
|
|
||||||
describe('supports', () => {
|
describe('supports', () => {
|
||||||
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
||||||
|
|
||||||
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
||||||
|
|
||||||
it('should support numeric strings',
|
it('should support numeric strings',
|
||||||
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
||||||
|
|
||||||
@ -42,88 +49,143 @@ export function main() {
|
|||||||
() => { expect(() => pipe.transform('123456789.11')).not.toThrow(); });
|
() => { expect(() => pipe.transform('123456789.11')).not.toThrow(); });
|
||||||
|
|
||||||
it('should support ISO string',
|
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', () => {
|
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
||||||
expect(() => pipe.transform({})).toThrow();
|
|
||||||
expect(() => pipe.transform('')).toThrow();
|
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', () => {
|
describe('transform', () => {
|
||||||
it('should format each component correctly', () => {
|
it('should format each component correctly', () => {
|
||||||
expect(pipe.transform(date, 'y')).toEqual('2015');
|
const dateFixtures: any = {
|
||||||
expect(pipe.transform(date, 'yy')).toEqual('15');
|
'y': '2015',
|
||||||
expect(pipe.transform(date, 'M')).toEqual('6');
|
'yy': '15',
|
||||||
expect(pipe.transform(date, 'MM')).toEqual('06');
|
'M': '6',
|
||||||
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
|
'MM': '06',
|
||||||
expect(pipe.transform(date, 'MMMM')).toEqual('June');
|
'MMM': 'Jun',
|
||||||
expect(pipe.transform(date, 'd')).toEqual('15');
|
'MMMM': 'June',
|
||||||
expect(pipe.transform(date, 'EEE')).toEqual('Mon');
|
'd': '15',
|
||||||
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
|
'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) {
|
if (!browserDetection.isOldChrome) {
|
||||||
expect(pipe.transform(date, 'h')).toEqual('9');
|
dateFixtures['h'] = '9';
|
||||||
expect(pipe.transform(date, 'hh')).toEqual('09');
|
dateFixtures['hh'] = '09';
|
||||||
expect(pipe.transform(date, 'j')).toEqual('9 AM');
|
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
|
// IE and Edge can't format a date to minutes and seconds without hours
|
||||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||||
!browserDetection.supportsNativeIntlApi) {
|
!browserDetection.supportsNativeIntlApi) {
|
||||||
if (!browserDetection.isOldChrome) {
|
if (!browserDetection.isOldChrome) {
|
||||||
expect(pipe.transform(date, 'HH')).toEqual('09');
|
dateFixtures['HH'] = '09';
|
||||||
|
isoStringWithoutTimeFixtures['HH'] = '00';
|
||||||
}
|
}
|
||||||
|
dateFixtures['E'] = 'M';
|
||||||
expect(pipe.transform(date, 'E')).toEqual('M');
|
dateFixtures['L'] = 'J';
|
||||||
expect(pipe.transform(date, 'L')).toEqual('J');
|
dateFixtures['m'] = '3';
|
||||||
expect(pipe.transform(date, 'm')).toEqual('3');
|
dateFixtures['s'] = '1';
|
||||||
expect(pipe.transform(date, 's')).toEqual('1');
|
dateFixtures['mm'] = '03';
|
||||||
expect(pipe.transform(date, 'mm')).toEqual('03');
|
dateFixtures['ss'] = '01';
|
||||||
expect(pipe.transform(date, 'ss')).toEqual('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();
|
expect(pipe.transform(date, 'Z')).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format common multi component patterns', () => {
|
it('should format common multi component patterns', () => {
|
||||||
expect(pipe.transform(date, 'EEE, M/d/y')).toEqual('Mon, 6/15/2015');
|
const dateFixtures: any = {
|
||||||
expect(pipe.transform(date, 'EEE, M/d')).toEqual('Mon, 6/15');
|
'EEE, M/d/y': 'Mon, 6/15/2015',
|
||||||
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
|
'EEE, M/d': 'Mon, 6/15',
|
||||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
'MMM d': 'Jun 15',
|
||||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
'dd/MM/yyyy': '15/06/2015',
|
||||||
expect(pipe.transform(date, 'yMEEEd')).toEqual('20156Mon15');
|
'MM/dd/yyyy': '06/15/2015',
|
||||||
expect(pipe.transform(date, 'MEEEd')).toEqual('6Mon15');
|
'yMEEEd': '20156Mon15',
|
||||||
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
|
'MEEEd': '6Mon15',
|
||||||
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
'MMMd': 'Jun15',
|
||||||
|
'yMMMMEEEEd': 'Monday, June 15, 2015'
|
||||||
|
};
|
||||||
|
|
||||||
// IE and Edge can't format a date to minutes and seconds without hours
|
// IE and Edge can't format a date to minutes and seconds without hours
|
||||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||||
!browserDetection.supportsNativeIntlApi) {
|
!browserDetection.supportsNativeIntlApi) {
|
||||||
expect(pipe.transform(date, 'ms')).toEqual('31');
|
dateFixtures['ms'] = '31';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!browserDetection.isOldChrome) {
|
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', () => {
|
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) {
|
if (!browserDetection.isOldChrome) {
|
||||||
// IE and Edge do not add a coma after the year in these 2 cases
|
// IE and Edge do not add a coma after the year in these 2 cases
|
||||||
if ((browserDetection.isEdge || browserDetection.isIE) &&
|
if ((browserDetection.isEdge || browserDetection.isIE) &&
|
||||||
browserDetection.supportsNativeIntlApi) {
|
browserDetection.supportsNativeIntlApi) {
|
||||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015 9:03:01 AM');
|
dateFixtures['medium'] = 'Jun 15, 2015 9:03:01 AM';
|
||||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015 9:03 AM');
|
dateFixtures['short'] = '6/15/2015 9:03 AM';
|
||||||
} else {
|
} else {
|
||||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
|
dateFixtures['medium'] = 'Jun 15, 2015, 9:03:01 AM';
|
||||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 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) {
|
if (!browserDetection.isOldChrome) {
|
||||||
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
|
dateFixtures['mediumTime'] = '9:03:01 AM';
|
||||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
|
dateFixtures['shortTime'] = '9:03 AM';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
|
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove bidi control characters',
|
it('should remove bidi control characters',
|
||||||
|
@ -12,10 +12,10 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('I18nPluralPipe', () => {
|
describe('I18nPluralPipe', () => {
|
||||||
var localization: NgLocalization;
|
let localization: NgLocalization;
|
||||||
var pipe: I18nPluralPipe;
|
let pipe: I18nPluralPipe;
|
||||||
|
|
||||||
var mapping = {
|
const mapping = {
|
||||||
'=0': 'No messages.',
|
'=0': 'No messages.',
|
||||||
'=1': 'One message.',
|
'=1': 'One message.',
|
||||||
'many': 'Many messages.',
|
'many': 'Many messages.',
|
||||||
@ -32,27 +32,27 @@ export function main() {
|
|||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return 0 text if value is 0', () => {
|
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.');
|
expect(val).toEqual('No messages.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 1 text if value is 1', () => {
|
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.');
|
expect(val).toEqual('One message.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return category messages', () => {
|
it('should return category messages', () => {
|
||||||
var val = pipe.transform(4, mapping);
|
const val = pipe.transform(4, mapping);
|
||||||
expect(val).toEqual('Many messages.');
|
expect(val).toEqual('Many messages.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should interpolate the value into the text where indicated', () => {
|
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.');
|
expect(val).toEqual('There are 6 messages, that is 6.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use "" if value is undefined', () => {
|
it('should use "" if value is undefined', () => {
|
||||||
var val = pipe.transform(void(0), mapping);
|
const val = pipe.transform(void(0), mapping);
|
||||||
expect(val).toEqual('');
|
expect(val).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,40 +8,35 @@
|
|||||||
|
|
||||||
import {I18nSelectPipe} from '@angular/common';
|
import {I18nSelectPipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('I18nSelectPipe', () => {
|
describe('I18nSelectPipe', () => {
|
||||||
var pipe: I18nSelectPipe;
|
const pipe: I18nSelectPipe = new I18nSelectPipe();
|
||||||
var mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
const mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||||
|
|
||||||
beforeEach(() => { pipe = new I18nSelectPipe(); });
|
|
||||||
|
|
||||||
it('should be marked as pure',
|
it('should be marked as pure',
|
||||||
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return male text if value is male', () => {
|
it('should return the "male" text if value is "male"', () => {
|
||||||
var val = pipe.transform('male', mapping);
|
const val = pipe.transform('male', mapping);
|
||||||
expect(val).toEqual('Invite him.');
|
expect(val).toEqual('Invite him.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return female text if value is female', () => {
|
it('should return the "female" text if value is "female"', () => {
|
||||||
var val = pipe.transform('female', mapping);
|
const val = pipe.transform('female', mapping);
|
||||||
expect(val).toEqual('Invite her.');
|
expect(val).toEqual('Invite her.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return "" if value is anything other than male or female', () => {
|
it('should return the "other" text if value is neither "male" nor "female"',
|
||||||
var val = pipe.transform('Anything else', mapping);
|
() => { expect(pipe.transform('Anything else', mapping)).toEqual('Invite them.'); });
|
||||||
expect(val).toEqual('');
|
|
||||||
|
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', () => {
|
it('should throw on bad arguments',
|
||||||
var val = pipe.transform(void(0), mapping);
|
|
||||||
expect(val).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not support bad arguments',
|
|
||||||
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('JsonPipe', () => {
|
describe('JsonPipe', () => {
|
||||||
var regNewLine = '\n';
|
const regNewLine = '\n';
|
||||||
var inceptionObj: any;
|
let inceptionObj: any;
|
||||||
var inceptionObjString: string;
|
let inceptionObjString: string;
|
||||||
var pipe: JsonPipe;
|
let pipe: JsonPipe;
|
||||||
|
|
||||||
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ export function main() {
|
|||||||
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
||||||
|
|
||||||
it('should return JSON-formatted string even when normalized', () => {
|
it('should return JSON-formatted string even when normalized', () => {
|
||||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
const dream1 = normalize(pipe.transform(inceptionObj));
|
||||||
var dream2 = normalize(inceptionObjString);
|
const dream2 = normalize(inceptionObjString);
|
||||||
expect(dream1).toEqual(dream2);
|
expect(dream1).toEqual(dream2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return JSON-formatted string similar to Json.stringify', () => {
|
it('should return JSON-formatted string similar to Json.stringify', () => {
|
||||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
const dream1 = normalize(pipe.transform(inceptionObj));
|
||||||
var dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
const dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
||||||
expect(dream1).toEqual(dream2);
|
expect(dream1).toEqual(dream2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -63,8 +63,8 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work with mutable objects', async(() => {
|
it('should work with mutable objects', async(() => {
|
||||||
let fixture = TestBed.createComponent(TestComp);
|
const fixture = TestBed.createComponent(TestComp);
|
||||||
let mutable: number[] = [1];
|
const mutable: number[] = [1];
|
||||||
fixture.componentInstance.data = mutable;
|
fixture.componentInstance.data = mutable;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
|
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() {
|
export function main() {
|
||||||
describe('LowerCasePipe', () => {
|
describe('LowerCasePipe', () => {
|
||||||
var upper: string;
|
let upper: string;
|
||||||
var lower: string;
|
let lower: string;
|
||||||
var pipe: LowerCasePipe;
|
let pipe: LowerCasePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
lower = 'something';
|
lower = 'something';
|
||||||
@ -23,14 +23,14 @@ export function main() {
|
|||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return lowercase', () => {
|
it('should return lowercase', () => {
|
||||||
var val = pipe.transform(upper);
|
const val = pipe.transform(upper);
|
||||||
expect(val).toEqual(lower);
|
expect(val).toEqual(lower);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should lowercase when there is a new value', () => {
|
it('should lowercase when there is a new value', () => {
|
||||||
var val = pipe.transform(upper);
|
const val = pipe.transform(upper);
|
||||||
expect(val).toEqual(lower);
|
expect(val).toEqual(lower);
|
||||||
var val2 = pipe.transform('WAT');
|
const val2 = pipe.transform('WAT');
|
||||||
expect(val2).toEqual('wat');
|
expect(val2).toEqual('wat');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
|||||||
export function main() {
|
export function main() {
|
||||||
describe('Number pipes', () => {
|
describe('Number pipes', () => {
|
||||||
describe('DecimalPipe', () => {
|
describe('DecimalPipe', () => {
|
||||||
var pipe: DecimalPipe;
|
let pipe: DecimalPipe;
|
||||||
|
|
||||||
beforeEach(() => { pipe = new DecimalPipe('en-US'); });
|
beforeEach(() => { pipe = new DecimalPipe('en-US'); });
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PercentPipe', () => {
|
describe('PercentPipe', () => {
|
||||||
var pipe: PercentPipe;
|
let pipe: PercentPipe;
|
||||||
|
|
||||||
beforeEach(() => { pipe = new PercentPipe('en-US'); });
|
beforeEach(() => { pipe = new PercentPipe('en-US'); });
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('CurrencyPipe', () => {
|
describe('CurrencyPipe', () => {
|
||||||
var pipe: CurrencyPipe;
|
let pipe: CurrencyPipe;
|
||||||
|
|
||||||
beforeEach(() => { pipe = new CurrencyPipe('en-US'); });
|
beforeEach(() => { pipe = new CurrencyPipe('en-US'); });
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('SlicePipe', () => {
|
describe('SlicePipe', () => {
|
||||||
var list: number[];
|
let list: number[];
|
||||||
var str: string;
|
let str: string;
|
||||||
var pipe: SlicePipe;
|
let pipe: SlicePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
list = [1, 2, 3, 4, 5];
|
list = [1, 2, 3, 4, 5];
|
||||||
@ -93,8 +93,8 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work with mutable arrays', async(() => {
|
it('should work with mutable arrays', async(() => {
|
||||||
let fixture = TestBed.createComponent(TestComp);
|
const fixture = TestBed.createComponent(TestComp);
|
||||||
let mutable: number[] = [1, 2];
|
const mutable: number[] = [1, 2];
|
||||||
fixture.componentInstance.data = mutable;
|
fixture.componentInstance.data = mutable;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('2');
|
expect(fixture.nativeElement).toHaveText('2');
|
||||||
|
@ -11,9 +11,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('UpperCasePipe', () => {
|
describe('UpperCasePipe', () => {
|
||||||
var upper: string;
|
let upper: string;
|
||||||
var lower: string;
|
let lower: string;
|
||||||
var pipe: UpperCasePipe;
|
let pipe: UpperCasePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
lower = 'something';
|
lower = 'something';
|
||||||
@ -24,14 +24,14 @@ export function main() {
|
|||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
|
|
||||||
it('should return uppercase', () => {
|
it('should return uppercase', () => {
|
||||||
var val = pipe.transform(lower);
|
const val = pipe.transform(lower);
|
||||||
expect(val).toEqual(upper);
|
expect(val).toEqual(upper);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should uppercase when there is a new value', () => {
|
it('should uppercase when there is a new value', () => {
|
||||||
var val = pipe.transform(lower);
|
const val = pipe.transform(lower);
|
||||||
expect(val).toEqual(upper);
|
expect(val).toEqual(upper);
|
||||||
var val2 = pipe.transform('wat');
|
const val2 = pipe.transform('wat');
|
||||||
expect(val2).toEqual('WAT');
|
expect(val2).toEqual('WAT');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ export class SpyLocation implements Location {
|
|||||||
path(): string { return this._history[this._historyIndex].path; }
|
path(): string { return this._history[this._historyIndex].path; }
|
||||||
|
|
||||||
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||||
var givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
const givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||||
var currPath =
|
const currPath =
|
||||||
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
||||||
|
|
||||||
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
||||||
@ -66,12 +66,12 @@ export class SpyLocation implements Location {
|
|||||||
this._history.push(new LocationState(path, query));
|
this._history.push(new LocationState(path, query));
|
||||||
this._historyIndex = this._history.length - 1;
|
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) {
|
if (locationState.path == path && locationState.query == query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.urlChanges.push(url);
|
this.urlChanges.push(url);
|
||||||
this._subject.emit({'url': url, 'pop': false});
|
this._subject.emit({'url': url, 'pop': false});
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ export class SpyLocation implements Location {
|
|||||||
replaceState(path: string, query: string = '') {
|
replaceState(path: string, query: string = '') {
|
||||||
path = this.prepareExternalUrl(path);
|
path = this.prepareExternalUrl(path);
|
||||||
|
|
||||||
var history = this._history[this._historyIndex];
|
const history = this._history[this._historyIndex];
|
||||||
if (history.path == path && history.query == query) {
|
if (history.path == path && history.query == query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ export class SpyLocation implements Location {
|
|||||||
history.path = path;
|
history.path = path;
|
||||||
history.query = query;
|
history.query = query;
|
||||||
|
|
||||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.urlChanges.push('replace: ' + url);
|
this.urlChanges.push('replace: ' + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,20 +44,20 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||||||
pushState(ctx: any, title: string, path: string, query: string): void {
|
pushState(ctx: any, title: string, path: string, query: string): void {
|
||||||
this.internalTitle = title;
|
this.internalTitle = title;
|
||||||
|
|
||||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.internalPath = url;
|
this.internalPath = url;
|
||||||
|
|
||||||
var externalUrl = this.prepareExternalUrl(url);
|
const externalUrl = this.prepareExternalUrl(url);
|
||||||
this.urlChanges.push(externalUrl);
|
this.urlChanges.push(externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceState(ctx: any, title: string, path: string, query: string): void {
|
replaceState(ctx: any, title: string, path: string, query: string): void {
|
||||||
this.internalTitle = title;
|
this.internalTitle = title;
|
||||||
|
|
||||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
const url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.internalPath = url;
|
this.internalPath = url;
|
||||||
|
|
||||||
var externalUrl = this.prepareExternalUrl(url);
|
const externalUrl = this.prepareExternalUrl(url);
|
||||||
this.urlChanges.push('replace: ' + externalUrl);
|
this.urlChanges.push('replace: ' + externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||||||
back(): void {
|
back(): void {
|
||||||
if (this.urlChanges.length > 0) {
|
if (this.urlChanges.length > 0) {
|
||||||
this.urlChanges.pop();
|
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);
|
this.simulatePopState(nextUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ApplicationRef, NgModule} from '@angular/core';
|
import {ApplicationRef, NgModule} from '@angular/core';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {ServerModule} from '@angular/platform-server';
|
||||||
import {MdButtonModule} from '@angular2-material/button';
|
import {MdButtonModule} from '@angular2-material/button';
|
||||||
|
|
||||||
import {ThirdpartyModule} from '../third_party_src/module';
|
import {ThirdpartyModule} from '../third_party_src/module';
|
||||||
@ -48,7 +48,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
|||||||
ComponentUsingThirdParty,
|
ComponentUsingThirdParty,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
ServerModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MdButtonModule,
|
MdButtonModule,
|
||||||
ModuleUsingCustomElements,
|
ModuleUsingCustomElements,
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import {LowerCasePipe, NgIf} from '@angular/common';
|
import {LowerCasePipe, NgIf} from '@angular/common';
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SomeService {
|
export class SomeService {
|
||||||
|
@ -19,9 +19,9 @@ describe('template codegen output', () => {
|
|||||||
|
|
||||||
it('should apply the animate states to the element', (done) => {
|
it('should apply the animate states to the element', (done) => {
|
||||||
const compFixture = createComponent(AnimateCmp);
|
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.componentInstance.setAsOpen();
|
||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
@ -45,9 +45,9 @@ describe('template codegen output', () => {
|
|||||||
|
|
||||||
it('should apply the default animate state to the element', (done) => {
|
it('should apply the default animate state to the element', (done) => {
|
||||||
const compFixture = createComponent(AnimateCmp);
|
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.componentInstance.setAsSomethingElse();
|
||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
|
@ -15,8 +15,8 @@ import {createComponent} from './util';
|
|||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
it('should support entryComponents in components', () => {
|
it('should support entryComponents in components', () => {
|
||||||
var compFixture = createComponent(CompWithEntryComponents);
|
const compFixture = createComponent(CompWithEntryComponents);
|
||||||
var cf = compFixture.componentInstance.cfr.resolveComponentFactory(BasicComp);
|
const cf = compFixture.componentInstance.cfr.resolveComponentFactory(BasicComp);
|
||||||
expect(cf.componentType).toBe(BasicComp);
|
expect(cf.componentType).toBe(BasicComp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ import {createComponent} from './util';
|
|||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
it('should support basic content projection', () => {
|
it('should support basic content projection', () => {
|
||||||
var mainCompFixture = createComponent(ProjectingComp);
|
const mainCompFixture = createComponent(ProjectingComp);
|
||||||
|
|
||||||
var debugElement = mainCompFixture.debugElement;
|
const debugElement = mainCompFixture.debugElement;
|
||||||
var compWithProjection = debugElement.query(By.directive(CompWithNgContent));
|
const compWithProjection = debugElement.query(By.directive(CompWithNgContent));
|
||||||
expect(compWithProjection.children.length).toBe(1);
|
expect(compWithProjection.children.length).toBe(1);
|
||||||
expect(compWithProjection.children[0].attributes['greeting']).toEqual('Hello world!');
|
expect(compWithProjection.children[0].attributes['greeting']).toEqual('Hello world!');
|
||||||
});
|
});
|
||||||
|
@ -14,18 +14,18 @@ import {createComponent} from './util';
|
|||||||
|
|
||||||
describe('child queries', () => {
|
describe('child queries', () => {
|
||||||
it('should support compiling child queries', () => {
|
it('should support compiling child queries', () => {
|
||||||
var childQueryCompFixture = createComponent(CompWithChildQuery);
|
const childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||||
var debugElement = childQueryCompFixture.debugElement;
|
const debugElement = childQueryCompFixture.debugElement;
|
||||||
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
const compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||||
expect(childQueryCompFixture.componentInstance.child).toBeDefined();
|
expect(childQueryCompFixture.componentInstance.child).toBeDefined();
|
||||||
expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true);
|
expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support compiling children queries', () => {
|
it('should support compiling children queries', () => {
|
||||||
var childQueryCompFixture = createComponent(CompWithChildQuery);
|
const childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||||
var debugElement = childQueryCompFixture.debugElement;
|
const debugElement = childQueryCompFixture.debugElement;
|
||||||
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
const compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||||
|
|
||||||
childQueryCompFixture.detectChanges();
|
childQueryCompFixture.detectChanges();
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
import * as compiler from '@angular/compiler';
|
import * as compiler from '@angular/compiler';
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
||||||
|
import {readFileSync} from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -22,14 +23,13 @@ import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
|||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||||
|
|
||||||
const nodeFs = require('fs');
|
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
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 GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
|
|
||||||
const PREAMBLE = `/**
|
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.
|
* Do not edit.
|
||||||
|
* @suppress {suspiciousCode,uselessCode,missingProperties}
|
||||||
*/
|
*/
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export class CodeGenerator {
|
|||||||
let root = this.options.basePath;
|
let root = this.options.basePath;
|
||||||
for (const eachRootDir of this.options.rootDirs || []) {
|
for (const eachRootDir of this.options.rootDirs || []) {
|
||||||
if (this.options.trace) {
|
if (this.options.trace) {
|
||||||
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||||
}
|
}
|
||||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||||
root = eachRootDir;
|
root = eachRootDir;
|
||||||
@ -99,7 +99,7 @@ export class CodeGenerator {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
`The translation file (${transFile}) locale must be provided. Use the --locale option.`);
|
`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();
|
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||||
@ -129,15 +129,16 @@ export class CodeGenerator {
|
|||||||
const resolver = new compiler.CompileMetadataResolver(
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
elementSchemaRegistry, staticReflector);
|
elementSchemaRegistry, normalizer, staticReflector);
|
||||||
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
const offlineCompiler = new compiler.OfflineCompiler(
|
||||||
resolver, normalizer, tmplParser, new compiler.StyleCompiler(urlResolver),
|
resolver, tmplParser, new compiler.StyleCompiler(urlResolver),
|
||||||
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
||||||
new compiler.DirectiveWrapperCompiler(
|
new compiler.DirectiveWrapperCompiler(
|
||||||
config, expressionParser, elementSchemaRegistry, console),
|
config, expressionParser, elementSchemaRegistry, console),
|
||||||
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
||||||
cliOptions.locale, cliOptions.i18nFormat);
|
cliOptions.locale, cliOptions.i18nFormat,
|
||||||
|
new compiler.AnimationParser(elementSchemaRegistry));
|
||||||
|
|
||||||
return new CodeGenerator(
|
return new CodeGenerator(
|
||||||
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
||||||
@ -160,7 +161,7 @@ export function extractProgramSymbols(
|
|||||||
|
|
||||||
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
|
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
console.log(`WARNING: no metadata found for ${absSrcPath}`);
|
console.warn(`WARNING: no metadata found for ${absSrcPath}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,4 +181,4 @@ export function extractProgramSymbols(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return staticSymbols;
|
return staticSymbols;
|
||||||
}
|
}
|
||||||
|
@ -28,50 +28,40 @@ export class Extractor {
|
|||||||
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
||||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||||
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
||||||
private metadataResolver: compiler.CompileMetadataResolver,
|
private metadataResolver: compiler.CompileMetadataResolver) {}
|
||||||
private directiveNormalizer: compiler.DirectiveNormalizer) {}
|
|
||||||
|
|
||||||
extract(): Promise<compiler.MessageBundle> {
|
extract(): Promise<compiler.MessageBundle> {
|
||||||
const programSymbols: StaticSymbol[] =
|
const programSymbols: StaticSymbol[] =
|
||||||
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
||||||
|
|
||||||
const files =
|
const {ngModules, files} = compiler.analyzeAndValidateNgModules(
|
||||||
compiler.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
|
programSymbols, {transitiveModules: true}, this.metadataResolver);
|
||||||
.files;
|
return compiler.loadNgModuleDirectives(ngModules).then(() => {
|
||||||
const errors: compiler.ParseError[] = [];
|
const errors: compiler.ParseError[] = [];
|
||||||
const filePromises: Promise<any>[] = [];
|
|
||||||
|
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
const cmpPromises: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
const compMetas: compiler.CompileDirectiveMetadata[] = [];
|
||||||
file.directives.forEach(directiveType => {
|
file.directives.forEach(directiveType => {
|
||||||
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta && dirMeta.isComponent) {
|
||||||
cmpPromises.push(this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult);
|
compMetas.push(dirMeta);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
compMetas.forEach(compMeta => {
|
||||||
|
const html = compMeta.template.template;
|
||||||
|
const interpolationConfig =
|
||||||
|
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
||||||
|
errors.push(
|
||||||
|
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cmpPromises.length) {
|
if (errors.length) {
|
||||||
const done =
|
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.messageBundle;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(filePromises).then(_ => this.messageBundle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(
|
static create(
|
||||||
@ -98,13 +88,12 @@ export class Extractor {
|
|||||||
const resolver = new compiler.CompileMetadataResolver(
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
elementSchemaRegistry, staticReflector);
|
elementSchemaRegistry, normalizer, staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
const messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
||||||
|
|
||||||
return new Extractor(
|
return new Extractor(
|
||||||
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver,
|
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver);
|
||||||
normalizer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
getCanonicalFileName(fileName: string): string {
|
getCanonicalFileName(fileName: string): string {
|
||||||
if (!fileName) return fileName;
|
if (!fileName) return fileName;
|
||||||
// NB: the rootDirs should have been sorted longest-first
|
// 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) {
|
if (fileName.indexOf(dir) === 0) {
|
||||||
fileName = fileName.substring(dir.length);
|
fileName = fileName.substring(dir.length);
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||||
}
|
}
|
||||||
return resolved.resolvedFileName;
|
return resolved.resolvedFileName;
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
containingFile = this.resolveAssetUrl(containingFile, '');
|
containingFile = this.resolveAssetUrl(containingFile, '');
|
||||||
|
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.log(
|
console.error(
|
||||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
|
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);
|
const parts = importModuleName.split(path.sep).filter(p => !!p);
|
||||||
let foundRelativeImport: string;
|
let foundRelativeImport: string;
|
||||||
for (let index = parts.length - 1; index >= 0; index--) {
|
for (let index = parts.length - 1; index >= 0; index--) {
|
||||||
|
@ -40,7 +40,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
||||||
|
|
||||||
this.context = context || new NodeReflectorHostContext(compilerHost);
|
this.context = context || new NodeReflectorHostContext(compilerHost);
|
||||||
var genPath: string = path.relative(this.basePath, this.genDir);
|
const genPath: string = path.relative(this.basePath, this.genDir);
|
||||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +67,13 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected normalizeAssetUrl(url: string): string {
|
protected normalizeAssetUrl(url: string): string {
|
||||||
let assetUrl = AssetUrl.parse(url);
|
const assetUrl = AssetUrl.parse(url);
|
||||||
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
||||||
return this.getCanonicalFileName(path);
|
return this.getCanonicalFileName(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected resolveAssetUrl(url: string, containingFile: string): string {
|
protected resolveAssetUrl(url: string, containingFile: string): string {
|
||||||
let assetUrl = this.normalizeAssetUrl(url);
|
const assetUrl = this.normalizeAssetUrl(url);
|
||||||
if (assetUrl) {
|
if (assetUrl) {
|
||||||
return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
|
return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
// drop extension
|
// drop extension
|
||||||
importedFile = importedFile.replace(EXT, '');
|
importedFile = importedFile.replace(EXT, '');
|
||||||
|
|
||||||
var nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
||||||
const importModule = nodeModulesIndex === -1 ?
|
const importModule = nodeModulesIndex === -1 ?
|
||||||
null :
|
null :
|
||||||
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
||||||
@ -141,7 +141,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private dotRelative(from: string, to: string): string {
|
private dotRelative(from: string, to: string): string {
|
||||||
var rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
||||||
return rPath.startsWith('.') ? rPath : './' + rPath;
|
return rPath.startsWith('.') ? rPath : './' + rPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
||||||
*/
|
*/
|
||||||
private rewriteGenDirPath(filepath: string) {
|
private rewriteGenDirPath(filepath: string) {
|
||||||
var nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
||||||
if (nodeModulesIndex !== -1) {
|
if (nodeModulesIndex !== -1) {
|
||||||
// If we are in node_modulse, transplant them into `genDir`.
|
// If we are in node_modulse, transplant them into `genDir`.
|
||||||
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
||||||
@ -172,7 +172,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let assetUrl = this.normalizeAssetUrl(module);
|
const assetUrl = this.normalizeAssetUrl(module);
|
||||||
if (assetUrl) {
|
if (assetUrl) {
|
||||||
module = assetUrl;
|
module = assetUrl;
|
||||||
}
|
}
|
||||||
@ -288,7 +288,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
}
|
}
|
||||||
return resolvedModulePath;
|
return resolvedModulePath;
|
||||||
};
|
};
|
||||||
let metadata = this.getResolverMetadata(filePath);
|
const metadata = this.getResolverMetadata(filePath);
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
// If we have metadata for the symbol, this is the original exporting location.
|
// If we have metadata for the symbol, this is the original exporting location.
|
||||||
if (metadata.metadata[symbolName]) {
|
if (metadata.metadata[symbolName]) {
|
||||||
|
@ -88,7 +88,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
public annotations(type: StaticSymbol): any[] {
|
public annotations(type: StaticSymbol): any[] {
|
||||||
let annotations = this.annotationCache.get(type);
|
let annotations = this.annotationCache.get(type);
|
||||||
if (!annotations) {
|
if (!annotations) {
|
||||||
let classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
if (classMetadata['decorators']) {
|
if (classMetadata['decorators']) {
|
||||||
annotations = this.simplify(type, classMetadata['decorators']);
|
annotations = this.simplify(type, classMetadata['decorators']);
|
||||||
} else {
|
} else {
|
||||||
@ -102,11 +102,11 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||||
let propMetadata = this.propertyCache.get(type);
|
let propMetadata = this.propertyCache.get(type);
|
||||||
if (!propMetadata) {
|
if (!propMetadata) {
|
||||||
let classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
let members = classMetadata ? classMetadata['members'] : {};
|
const members = classMetadata ? classMetadata['members'] : {};
|
||||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||||
let prop = (<any[]>propData)
|
const prop = (<any[]>propData)
|
||||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||||
if (prop && prop['decorators']) {
|
if (prop && prop['decorators']) {
|
||||||
return this.simplify(type, prop['decorators']);
|
return this.simplify(type, prop['decorators']);
|
||||||
} else {
|
} else {
|
||||||
@ -125,21 +125,20 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
try {
|
try {
|
||||||
let parameters = this.parameterCache.get(type);
|
let parameters = this.parameterCache.get(type);
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
let classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
let members = classMetadata ? classMetadata['members'] : null;
|
const members = classMetadata ? classMetadata['members'] : null;
|
||||||
let ctorData = members ? members['__ctor__'] : null;
|
const ctorData = members ? members['__ctor__'] : null;
|
||||||
if (ctorData) {
|
if (ctorData) {
|
||||||
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
const ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||||
let parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
const parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
||||||
let parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
const parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
||||||
|
|
||||||
parameters = [];
|
parameters = [];
|
||||||
parameterTypes.forEach((paramType, index) => {
|
parameterTypes.forEach((paramType, index) => {
|
||||||
let nestedResult: any[] = [];
|
const nestedResult: any[] = [];
|
||||||
if (paramType) {
|
if (paramType) {
|
||||||
nestedResult.push(paramType);
|
nestedResult.push(paramType);
|
||||||
}
|
}
|
||||||
let decorators = parameterDecorators ? parameterDecorators[index] : null;
|
const decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||||
if (decorators) {
|
if (decorators) {
|
||||||
nestedResult.push(...decorators);
|
nestedResult.push(...decorators);
|
||||||
}
|
}
|
||||||
@ -153,7 +152,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
return parameters;
|
return parameters;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,7 +272,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
function simplifyCall(expression: any) {
|
function simplifyCall(expression: any) {
|
||||||
let callContext: {[name: string]: string}|undefined = undefined;
|
let callContext: {[name: string]: string}|undefined = undefined;
|
||||||
if (expression['__symbolic'] == 'call') {
|
if (expression['__symbolic'] == 'call') {
|
||||||
let target = expression['expression'];
|
const target = expression['expression'];
|
||||||
let functionSymbol: StaticSymbol;
|
let functionSymbol: StaticSymbol;
|
||||||
let targetFunction: any;
|
let targetFunction: any;
|
||||||
if (target) {
|
if (target) {
|
||||||
@ -316,7 +315,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
for (let i = 0; i < parameters.length; i++) {
|
for (let i = 0; i < parameters.length; i++) {
|
||||||
functionScope.define(parameters[i], args[i]);
|
functionScope.define(parameters[i], args[i]);
|
||||||
}
|
}
|
||||||
let oldScope = scope;
|
const oldScope = scope;
|
||||||
let result: any;
|
let result: any;
|
||||||
try {
|
try {
|
||||||
scope = functionScope.done();
|
scope = functionScope.done();
|
||||||
@ -347,19 +346,19 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
if (expression instanceof Array) {
|
if (expression instanceof Array) {
|
||||||
let result: any[] = [];
|
const result: any[] = [];
|
||||||
for (let item of (<any>expression)) {
|
for (const item of (<any>expression)) {
|
||||||
// Check for a spread expression
|
// Check for a spread expression
|
||||||
if (item && item.__symbolic === 'spread') {
|
if (item && item.__symbolic === 'spread') {
|
||||||
let spreadArray = simplify(item.expression);
|
const spreadArray = simplify(item.expression);
|
||||||
if (Array.isArray(spreadArray)) {
|
if (Array.isArray(spreadArray)) {
|
||||||
for (let spreadItem of spreadArray) {
|
for (const spreadItem of spreadArray) {
|
||||||
result.push(spreadItem);
|
result.push(spreadItem);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let value = simplify(item);
|
const value = simplify(item);
|
||||||
if (shouldIgnore(value)) {
|
if (shouldIgnore(value)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -466,8 +465,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return null;
|
return null;
|
||||||
case 'reference':
|
case 'reference':
|
||||||
if (!expression.module) {
|
if (!expression.module) {
|
||||||
let name: string = expression['name'];
|
const name: string = expression['name'];
|
||||||
let localValue = scope.resolve(name);
|
const localValue = scope.resolve(name);
|
||||||
if (localValue != BindingScope.missing) {
|
if (localValue != BindingScope.missing) {
|
||||||
return localValue;
|
return localValue;
|
||||||
}
|
}
|
||||||
@ -516,6 +515,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (expression['line']) {
|
if (expression['line']) {
|
||||||
message =
|
message =
|
||||||
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
|
`${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);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
@ -529,7 +530,11 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
try {
|
try {
|
||||||
return simplify(value);
|
return simplify(value);
|
||||||
} catch (e) {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +613,7 @@ function mapStringMap(input: {[key: string]: any}, transform: (value: any, key:
|
|||||||
if (!input) return {};
|
if (!input) return {};
|
||||||
const result: {[key: string]: any} = {};
|
const result: {[key: string]: any} = {};
|
||||||
Object.keys(input).forEach((key) => {
|
Object.keys(input).forEach((key) => {
|
||||||
let value = transform(input[key], key);
|
const value = transform(input[key], key);
|
||||||
if (!shouldIgnore(value)) {
|
if (!shouldIgnore(value)) {
|
||||||
result[key] = value;
|
result[key] = value;
|
||||||
}
|
}
|
||||||
@ -659,3 +664,11 @@ function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
|||||||
function shouldIgnore(value: any): boolean {
|
function shouldIgnore(value: any): boolean {
|
||||||
return value && value.__symbolic == 'ignore';
|
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;
|
||||||
|
}
|
@ -21,7 +21,7 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
directoryExists(path: string): boolean { return typeof this.getEntry(path) === 'object'; }
|
directoryExists(path: string): boolean { return typeof this.getEntry(path) === 'object'; }
|
||||||
|
|
||||||
readFile(fileName: string): string|undefined {
|
readFile(fileName: string): string|undefined {
|
||||||
let data = this.getEntry(fileName);
|
const data = this.getEntry(fileName);
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -29,9 +29,9 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeFile(fileName: string, data: string): void {
|
writeFile(fileName: string, data: string): void {
|
||||||
let parts = fileName.split('/');
|
const parts = fileName.split('/');
|
||||||
let name = parts.pop();
|
const name = parts.pop();
|
||||||
let entry = this.getEntry(parts);
|
const entry = this.getEntry(parts);
|
||||||
if (entry && typeof entry !== 'string') {
|
if (entry && typeof entry !== 'string') {
|
||||||
entry[name] = data;
|
entry[name] = data;
|
||||||
}
|
}
|
||||||
@ -48,11 +48,11 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
parts = normalize(parts);
|
parts = normalize(parts);
|
||||||
let current = this.files;
|
let current = this.files;
|
||||||
while (parts.length) {
|
while (parts.length) {
|
||||||
let part = parts.shift();
|
const part = parts.shift();
|
||||||
if (typeof current === 'string') {
|
if (typeof current === 'string') {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
let next = (<Directory>current)[part];
|
const next = (<Directory>current)[part];
|
||||||
if (next === undefined) {
|
if (next === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -72,9 +72,9 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalize(parts: string[]): string[] {
|
function normalize(parts: string[]): string[] {
|
||||||
let result: string[] = [];
|
const result: string[] = [];
|
||||||
while (parts.length) {
|
while (parts.length) {
|
||||||
let part = parts.shift();
|
const part = parts.shift();
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case '.':
|
case '.':
|
||||||
break;
|
break;
|
||||||
@ -102,7 +102,7 @@ export class MockCompilerHost implements ts.CompilerHost {
|
|||||||
getSourceFile(
|
getSourceFile(
|
||||||
fileName: string, languageVersion: ts.ScriptTarget,
|
fileName: string, languageVersion: ts.ScriptTarget,
|
||||||
onError?: (message: string) => void): ts.SourceFile {
|
onError?: (message: string) => void): ts.SourceFile {
|
||||||
let sourceText = this.context.readFile(fileName);
|
const sourceText = this.context.readFile(fileName);
|
||||||
if (sourceText) {
|
if (sourceText) {
|
||||||
return ts.createSourceFile(fileName, sourceText, languageVersion);
|
return ts.createSourceFile(fileName, sourceText, languageVersion);
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,11 +14,11 @@ import {ReflectorHost} from '../src/reflector_host';
|
|||||||
import {Directory, Entry, MockCompilerHost, MockContext} from './mocks';
|
import {Directory, Entry, MockCompilerHost, MockContext} from './mocks';
|
||||||
|
|
||||||
describe('reflector_host', () => {
|
describe('reflector_host', () => {
|
||||||
var context: MockContext;
|
let context: MockContext;
|
||||||
var host: ts.CompilerHost;
|
let host: ts.CompilerHost;
|
||||||
var program: ts.Program;
|
let program: ts.Program;
|
||||||
var reflectorNestedGenDir: ReflectorHost;
|
let reflectorNestedGenDir: ReflectorHost;
|
||||||
var reflectorSiblingGenDir: ReflectorHost;
|
let reflectorSiblingGenDir: ReflectorHost;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
context = new MockContext('/tmp/src', clone(FILES));
|
context = new MockContext('/tmp/src', clone(FILES));
|
||||||
@ -29,7 +29,7 @@ describe('reflector_host', () => {
|
|||||||
},
|
},
|
||||||
host);
|
host);
|
||||||
// Force a typecheck
|
// Force a typecheck
|
||||||
let errors = program.getSemanticDiagnostics();
|
const errors = program.getSemanticDiagnostics();
|
||||||
if (errors && errors.length) {
|
if (errors && errors.length) {
|
||||||
throw new Error('Expected no errors');
|
throw new Error('Expected no errors');
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ describe('reflector_host', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should provide the import locations for angular', () => {
|
it('should provide the import locations for angular', () => {
|
||||||
let {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} =
|
const {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} =
|
||||||
reflectorNestedGenDir.angularImportLocations();
|
reflectorNestedGenDir.angularImportLocations();
|
||||||
expect(coreDecorators).toEqual('@angular/core/src/metadata');
|
expect(coreDecorators).toEqual('@angular/core/src/metadata');
|
||||||
expect(diDecorators).toEqual('@angular/core/src/di/metadata');
|
expect(diDecorators).toEqual('@angular/core/src/di/metadata');
|
||||||
@ -163,8 +163,8 @@ describe('reflector_host', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should be produce the same symbol if asked twice', () => {
|
it('should be produce the same symbol if asked twice', () => {
|
||||||
let foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
const foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
||||||
let foo2 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
const foo2 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
|
||||||
expect(foo1).toBe(foo2);
|
expect(foo1).toBe(foo2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -320,8 +320,8 @@ function clone(entry: Entry): Entry {
|
|||||||
if (typeof entry === 'string') {
|
if (typeof entry === 'string') {
|
||||||
return entry;
|
return entry;
|
||||||
} else {
|
} else {
|
||||||
let result: Directory = {};
|
const result: Directory = {};
|
||||||
for (let name in entry) {
|
for (const name in entry) {
|
||||||
result[name] = clone(entry[name]);
|
result[name] = clone(entry[name]);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector';
|
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector';
|
||||||
import {HostListener, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||||
import {MetadataCollector} from '@angular/tsc-wrapped';
|
import {MetadataCollector} from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ import * as ts from 'typescript';
|
|||||||
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
|
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
|
||||||
|
|
||||||
describe('StaticReflector', () => {
|
describe('StaticReflector', () => {
|
||||||
let noContext = new StaticSymbol('', '');
|
const noContext = new StaticSymbol('', '');
|
||||||
let host: StaticReflectorHost;
|
let host: StaticReflectorHost;
|
||||||
let reflector: StaticReflector;
|
let reflector: StaticReflector;
|
||||||
|
|
||||||
@ -31,36 +31,37 @@ describe('StaticReflector', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('should get annotations for NgFor', () => {
|
it('should get annotations for NgFor', () => {
|
||||||
let NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
const NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
||||||
let annotations = reflector.annotations(NgFor);
|
const annotations = reflector.annotations(NgFor);
|
||||||
expect(annotations.length).toEqual(1);
|
expect(annotations.length).toEqual(1);
|
||||||
let annotation = annotations[0];
|
const annotation = annotations[0];
|
||||||
expect(annotation.selector).toEqual('[ngFor][ngForOf]');
|
expect(annotation.selector).toEqual('[ngFor][ngForOf]');
|
||||||
expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']);
|
expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get constructor for NgFor', () => {
|
it('should get constructor for NgFor', () => {
|
||||||
let NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
const NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
||||||
let ViewContainerRef =
|
const ViewContainerRef =
|
||||||
host.findDeclaration('angular2/src/core/linker/view_container_ref', 'ViewContainerRef');
|
host.findDeclaration('angular2/src/core/linker/view_container_ref', 'ViewContainerRef');
|
||||||
let TemplateRef = host.findDeclaration('angular2/src/core/linker/template_ref', 'TemplateRef');
|
const TemplateRef =
|
||||||
let IterableDiffers = host.findDeclaration(
|
host.findDeclaration('angular2/src/core/linker/template_ref', 'TemplateRef');
|
||||||
|
const IterableDiffers = host.findDeclaration(
|
||||||
'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers');
|
'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers');
|
||||||
let ChangeDetectorRef = host.findDeclaration(
|
const ChangeDetectorRef = host.findDeclaration(
|
||||||
'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef');
|
'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef');
|
||||||
|
|
||||||
let parameters = reflector.parameters(NgFor);
|
const parameters = reflector.parameters(NgFor);
|
||||||
expect(parameters).toEqual([
|
expect(parameters).toEqual([
|
||||||
[ViewContainerRef], [TemplateRef], [IterableDiffers], [ChangeDetectorRef]
|
[ViewContainerRef], [TemplateRef], [IterableDiffers], [ChangeDetectorRef]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get annotations for HeroDetailComponent', () => {
|
it('should get annotations for HeroDetailComponent', () => {
|
||||||
let HeroDetailComponent =
|
const HeroDetailComponent =
|
||||||
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||||
let annotations = reflector.annotations(HeroDetailComponent);
|
const annotations = reflector.annotations(HeroDetailComponent);
|
||||||
expect(annotations.length).toEqual(1);
|
expect(annotations.length).toEqual(1);
|
||||||
let annotation = annotations[0];
|
const annotation = annotations[0];
|
||||||
expect(annotation.selector).toEqual('my-hero-detail');
|
expect(annotation.selector).toEqual('my-hero-detail');
|
||||||
expect(annotation.animations).toEqual([trigger('myAnimation', [
|
expect(annotation.animations).toEqual([trigger('myAnimation', [
|
||||||
state('state1', style({'background': 'white'})),
|
state('state1', style({'background': 'white'})),
|
||||||
@ -73,40 +74,40 @@ describe('StaticReflector', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw and exception for unsupported metadata versions', () => {
|
it('should throw and exception for unsupported metadata versions', () => {
|
||||||
let e = host.findDeclaration('src/version-error', 'e');
|
const e = host.findDeclaration('src/version-error', 'e');
|
||||||
expect(() => reflector.annotations(e))
|
expect(() => reflector.annotations(e))
|
||||||
.toThrow(new Error(
|
.toThrow(new Error(
|
||||||
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1'));
|
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get and empty annotation list for an unknown class', () => {
|
it('should get and empty annotation list for an unknown class', () => {
|
||||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
const UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||||
let annotations = reflector.annotations(UnknownClass);
|
const annotations = reflector.annotations(UnknownClass);
|
||||||
expect(annotations).toEqual([]);
|
expect(annotations).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get propMetadata for HeroDetailComponent', () => {
|
it('should get propMetadata for HeroDetailComponent', () => {
|
||||||
let HeroDetailComponent =
|
const HeroDetailComponent =
|
||||||
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||||
let props = reflector.propMetadata(HeroDetailComponent);
|
const props = reflector.propMetadata(HeroDetailComponent);
|
||||||
expect(props['hero']).toBeTruthy();
|
expect(props['hero']).toBeTruthy();
|
||||||
expect(props['onMouseOver']).toEqual([new HostListener('mouseover', ['$event'])]);
|
expect(props['onMouseOver']).toEqual([new HostListener('mouseover', ['$event'])]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get an empty object from propMetadata for an unknown class', () => {
|
it('should get an empty object from propMetadata for an unknown class', () => {
|
||||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
const UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||||
let properties = reflector.propMetadata(UnknownClass);
|
const properties = reflector.propMetadata(UnknownClass);
|
||||||
expect(properties).toEqual({});
|
expect(properties).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get empty parameters list for an unknown class ', () => {
|
it('should get empty parameters list for an unknown class ', () => {
|
||||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
const UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||||
let parameters = reflector.parameters(UnknownClass);
|
const parameters = reflector.parameters(UnknownClass);
|
||||||
expect(parameters).toEqual([]);
|
expect(parameters).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should provide context for errors reported by the collector', () => {
|
it('should provide context for errors reported by the collector', () => {
|
||||||
let SomeClass = host.findDeclaration('src/error-reporting', 'SomeClass');
|
const SomeClass = host.findDeclaration('src/error-reporting', 'SomeClass');
|
||||||
expect(() => reflector.annotations(SomeClass))
|
expect(() => reflector.annotations(SomeClass))
|
||||||
.toThrow(new Error(
|
.toThrow(new Error(
|
||||||
'Error encountered resolving symbol values statically. A reasonable error message (position 13:34 in the original .ts file), resolving symbol ErrorSym in /tmp/src/error-references.d.ts, resolving symbol Link2 in /tmp/src/error-references.d.ts, resolving symbol Link1 in /tmp/src/error-references.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts'));
|
'Error encountered resolving symbol values statically. A reasonable error message (position 13:34 in the original .ts file), resolving symbol ErrorSym in /tmp/src/error-references.d.ts, resolving symbol Link2 in /tmp/src/error-references.d.ts, resolving symbol Link1 in /tmp/src/error-references.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts'));
|
||||||
@ -128,7 +129,7 @@ describe('StaticReflector', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify an object to a copy of the object', () => {
|
it('should simplify an object to a copy of the object', () => {
|
||||||
let expr = {a: 1, b: 2, c: 3};
|
const expr = {a: 1, b: 2, c: 3};
|
||||||
expect(simplify(noContext, expr)).toEqual(expr);
|
expect(simplify(noContext, expr)).toEqual(expr);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ describe('StaticReflector', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should simplify an object index', () => {
|
it('should simplify an object index', () => {
|
||||||
let expr = {__symbolic: 'select', expression: {a: 1, b: 2, c: 3}, member: 'b'};
|
const expr = {__symbolic: 'select', expression: {a: 1, b: 2, c: 3}, member: 'b'};
|
||||||
expect(simplify(noContext, expr)).toBe(2);
|
expect(simplify(noContext, expr)).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -337,6 +338,25 @@ describe('StaticReflector', () => {
|
|||||||
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should record data about the error in the exception', () => {
|
||||||
|
let threw = false;
|
||||||
|
try {
|
||||||
|
const metadata = host.getMetadataFor('/tmp/src/invalid-metadata.ts');
|
||||||
|
expect(metadata).toBeDefined();
|
||||||
|
if (!Array.isArray(metadata)) {
|
||||||
|
const moduleMetadata: any = metadata['metadata'];
|
||||||
|
expect(moduleMetadata).toBeDefined();
|
||||||
|
const classData: any = moduleMetadata['InvalidMetadata'];
|
||||||
|
expect(classData).toBeDefined();
|
||||||
|
simplify(new StaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
expect(threw).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should error on indirect recursive calls', () => {
|
it('should error on indirect recursive calls', () => {
|
||||||
expect(
|
expect(
|
||||||
() => simplify(
|
() => simplify(
|
||||||
@ -354,7 +374,7 @@ describe('StaticReflector', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to get metadata from a ts file', () => {
|
it('should be able to get metadata from a ts file', () => {
|
||||||
let metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
|
const metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 1,
|
||||||
@ -385,11 +405,18 @@ describe('StaticReflector', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to get metadata for a class containing a custom decorator', () => {
|
it('should be able to get metadata for a class containing a custom decorator', () => {
|
||||||
let props = reflector.propMetadata(
|
const props = reflector.propMetadata(
|
||||||
host.getStaticSymbol('/tmp/src/custom-decorator-reference.ts', 'Foo'));
|
host.getStaticSymbol('/tmp/src/custom-decorator-reference.ts', 'Foo'));
|
||||||
expect(props).toEqual({foo: []});
|
expect(props).toEqual({foo: []});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should read ctor parameters with forwardRef', () => {
|
||||||
|
const src = '/tmp/src/forward-ref.ts';
|
||||||
|
const dep = host.getStaticSymbol(src, 'Dep');
|
||||||
|
const props = reflector.parameters(host.getStaticSymbol(src, 'Forward'));
|
||||||
|
expect(props).toEqual([[dep, new Inject(dep)]]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should report an error for invalid function calls', () => {
|
it('should report an error for invalid function calls', () => {
|
||||||
expect(
|
expect(
|
||||||
() =>
|
() =>
|
||||||
@ -458,8 +485,8 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||||
|
|
||||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||||
var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
|
const cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
|
||||||
var result = this.staticTypeCache.get(cacheKey);
|
let result = this.staticTypeCache.get(cacheKey);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = new StaticSymbol(declarationFile, name, members);
|
result = new StaticSymbol(declarationFile, name, members);
|
||||||
this.staticTypeCache.set(cacheKey, result);
|
this.staticTypeCache.set(cacheKey, result);
|
||||||
@ -472,7 +499,7 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
|
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
|
||||||
|
|
||||||
function resolvePath(pathParts: string[]): string {
|
function resolvePath(pathParts: string[]): string {
|
||||||
let result: string[] = [];
|
const result: string[] = [];
|
||||||
pathParts.forEach((part, index) => {
|
pathParts.forEach((part, index) => {
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case '':
|
case '':
|
||||||
@ -491,9 +518,9 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
function pathTo(from: string, to: string): string {
|
function pathTo(from: string, to: string): string {
|
||||||
let result = to;
|
let result = to;
|
||||||
if (to.startsWith('.')) {
|
if (to.startsWith('.')) {
|
||||||
let fromParts = splitPath(from);
|
const fromParts = splitPath(from);
|
||||||
fromParts.pop(); // remove the file name.
|
fromParts.pop(); // remove the file name.
|
||||||
let toParts = splitPath(to);
|
const toParts = splitPath(to);
|
||||||
result = resolvePath(fromParts.concat(toParts));
|
result = resolvePath(fromParts.concat(toParts));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -511,7 +538,7 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMetadataFor(moduleId: string): any {
|
getMetadataFor(moduleId: string): any {
|
||||||
let data: {[key: string]: any} = {
|
const data: {[key: string]: any} = {
|
||||||
'/tmp/angular2/src/common/forms-deprecated/directives.d.ts': [{
|
'/tmp/angular2/src/common/forms-deprecated/directives.d.ts': [{
|
||||||
'__symbolic': 'module',
|
'__symbolic': 'module',
|
||||||
'version': 1,
|
'version': 1,
|
||||||
@ -1040,15 +1067,36 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
export class MethodReference {
|
export class MethodReference {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
'/tmp/src/invalid-metadata.ts': `
|
||||||
|
import {Component} from 'angular2/src/core/metadata';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
providers: [ { provider: 'a', useValue: (() => 1)() }]
|
||||||
|
})
|
||||||
|
export class InvalidMetadata {}
|
||||||
|
`,
|
||||||
|
'/tmp/src/forward-ref.ts': `
|
||||||
|
import {forwardRef} from 'angular2/core';
|
||||||
|
import {Component} from 'angular2/src/core/metadata';
|
||||||
|
import {Inject} from 'angular2/src/core/di/metadata';
|
||||||
|
@Component({})
|
||||||
|
export class Forward {
|
||||||
|
constructor(@Inject(forwardRef(() => Dep)) d: Dep) {}
|
||||||
|
}
|
||||||
|
export class Dep {
|
||||||
|
@Input f: Forward;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (data[moduleId] && moduleId.match(TS_EXT)) {
|
if (data[moduleId] && moduleId.match(TS_EXT)) {
|
||||||
let text = data[moduleId];
|
const text = data[moduleId];
|
||||||
if (typeof text === 'string') {
|
if (typeof text === 'string') {
|
||||||
let sf = ts.createSourceFile(moduleId, data[moduleId], ts.ScriptTarget.ES5);
|
const sf = ts.createSourceFile(
|
||||||
let diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
|
moduleId, data[moduleId], ts.ScriptTarget.ES5, /* setParentNodes */ true);
|
||||||
|
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
|
||||||
if (diagnostics && diagnostics.length) {
|
if (diagnostics && diagnostics.length) {
|
||||||
throw Error(`Error encountered during parse of file ${moduleId}`);
|
throw Error(`Error encountered during parse of file ${moduleId}`);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es5",
|
|
||||||
"lib": ["es6", "dom"],
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"declaration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
@ -14,11 +14,11 @@
|
|||||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||||
},
|
},
|
||||||
"experimentalDecorators": true,
|
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"sourceRoot": ".",
|
"sourceMap": true,
|
||||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
"inlineSources": true,
|
||||||
"declaration": true,
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"exclude": ["integrationtest"],
|
"exclude": ["integrationtest"],
|
||||||
|
@ -52,5 +52,6 @@ export * from './src/selector';
|
|||||||
export * from './src/style_compiler';
|
export * from './src/style_compiler';
|
||||||
export * from './src/template_parser/template_parser';
|
export * from './src/template_parser/template_parser';
|
||||||
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
||||||
|
export {AnimationParser} from './src/animation/animation_parser';
|
||||||
|
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
||||||
|
@ -29,19 +29,21 @@ export class AnimationCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ANIMATION_FACTORY_ELEMENT_VAR = o.variable('element');
|
const _ANIMATION_FACTORY_ELEMENT_VAR = o.variable('element');
|
||||||
var _ANIMATION_DEFAULT_STATE_VAR = o.variable('defaultStateStyles');
|
const _ANIMATION_DEFAULT_STATE_VAR = o.variable('defaultStateStyles');
|
||||||
var _ANIMATION_FACTORY_VIEW_VAR = o.variable('view');
|
const _ANIMATION_FACTORY_VIEW_VAR = o.variable('view');
|
||||||
var _ANIMATION_FACTORY_VIEW_CONTEXT = _ANIMATION_FACTORY_VIEW_VAR.prop('animationContext');
|
const _ANIMATION_FACTORY_VIEW_CONTEXT = _ANIMATION_FACTORY_VIEW_VAR.prop('animationContext');
|
||||||
var _ANIMATION_FACTORY_RENDERER_VAR = _ANIMATION_FACTORY_VIEW_VAR.prop('renderer');
|
const _ANIMATION_FACTORY_RENDERER_VAR = _ANIMATION_FACTORY_VIEW_VAR.prop('renderer');
|
||||||
var _ANIMATION_CURRENT_STATE_VAR = o.variable('currentState');
|
const _ANIMATION_CURRENT_STATE_VAR = o.variable('currentState');
|
||||||
var _ANIMATION_NEXT_STATE_VAR = o.variable('nextState');
|
const _ANIMATION_NEXT_STATE_VAR = o.variable('nextState');
|
||||||
var _ANIMATION_PLAYER_VAR = o.variable('player');
|
const _ANIMATION_PLAYER_VAR = o.variable('player');
|
||||||
var _ANIMATION_TIME_VAR = o.variable('totalTime');
|
const _ANIMATION_TIME_VAR = o.variable('totalTime');
|
||||||
var _ANIMATION_START_STATE_STYLES_VAR = o.variable('startStateStyles');
|
const _ANIMATION_START_STATE_STYLES_VAR = o.variable('startStateStyles');
|
||||||
var _ANIMATION_END_STATE_STYLES_VAR = o.variable('endStateStyles');
|
const _ANIMATION_END_STATE_STYLES_VAR = o.variable('endStateStyles');
|
||||||
var _ANIMATION_COLLECTED_STYLES = o.variable('collectedStyles');
|
const _ANIMATION_COLLECTED_STYLES = o.variable('collectedStyles');
|
||||||
var EMPTY_MAP = o.literalMap([]);
|
const _PREVIOUS_ANIMATION_PLAYERS = o.variable('previousPlayers');
|
||||||
|
const _EMPTY_MAP = o.literalMap([]);
|
||||||
|
const _EMPTY_ARRAY = o.literalArr([]);
|
||||||
|
|
||||||
class _AnimationBuilder implements AnimationAstVisitor {
|
class _AnimationBuilder implements AnimationAstVisitor {
|
||||||
private _fnVarName: string;
|
private _fnVarName: string;
|
||||||
@ -55,7 +57,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitAnimationStyles(ast: AnimationStylesAst, context: _AnimationBuilderContext): o.Expression {
|
visitAnimationStyles(ast: AnimationStylesAst, context: _AnimationBuilderContext): o.Expression {
|
||||||
var stylesArr: any[] = [];
|
const stylesArr: any[] = [];
|
||||||
if (context.isExpectingFirstStyleStep) {
|
if (context.isExpectingFirstStyleStep) {
|
||||||
stylesArr.push(_ANIMATION_START_STATE_STYLES_VAR);
|
stylesArr.push(_ANIMATION_START_STATE_STYLES_VAR);
|
||||||
context.isExpectingFirstStyleStep = false;
|
context.isExpectingFirstStyleStep = false;
|
||||||
@ -86,8 +88,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
return this._visitEndStateAnimation(ast, context);
|
return this._visitEndStateAnimation(ast, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
var startingStylesExpr = ast.startingStyles.visit(this, context);
|
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||||
var keyframeExpressions =
|
const keyframeExpressions =
|
||||||
ast.keyframes.map(keyframeEntry => keyframeEntry.visit(this, context));
|
ast.keyframes.map(keyframeEntry => keyframeEntry.visit(this, context));
|
||||||
return this._callAnimateMethod(
|
return this._callAnimateMethod(
|
||||||
ast, startingStylesExpr, o.literalArr(keyframeExpressions), context);
|
ast, startingStylesExpr, o.literalArr(keyframeExpressions), context);
|
||||||
@ -95,9 +97,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_visitEndStateAnimation(ast: AnimationStepAst, context: _AnimationBuilderContext): o.Expression {
|
_visitEndStateAnimation(ast: AnimationStepAst, context: _AnimationBuilderContext): o.Expression {
|
||||||
var startingStylesExpr = ast.startingStyles.visit(this, context);
|
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||||
var keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
||||||
var keyframesExpr =
|
const keyframesExpr =
|
||||||
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
||||||
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
||||||
o.literalArr(keyframeExpressions)
|
o.literalArr(keyframeExpressions)
|
||||||
@ -110,23 +112,28 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
_callAnimateMethod(
|
_callAnimateMethod(
|
||||||
ast: AnimationStepAst, startingStylesExpr: any, keyframesExpr: any,
|
ast: AnimationStepAst, startingStylesExpr: any, keyframesExpr: any,
|
||||||
context: _AnimationBuilderContext) {
|
context: _AnimationBuilderContext) {
|
||||||
|
let previousStylesValue: o.Expression = _EMPTY_ARRAY;
|
||||||
|
if (context.isExpectingFirstAnimateStep) {
|
||||||
|
previousStylesValue = _PREVIOUS_ANIMATION_PLAYERS;
|
||||||
|
context.isExpectingFirstAnimateStep = false;
|
||||||
|
}
|
||||||
context.totalTransitionTime += ast.duration + ast.delay;
|
context.totalTransitionTime += ast.duration + ast.delay;
|
||||||
return _ANIMATION_FACTORY_RENDERER_VAR.callMethod('animate', [
|
return _ANIMATION_FACTORY_RENDERER_VAR.callMethod('animate', [
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, startingStylesExpr, keyframesExpr, o.literal(ast.duration),
|
_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):
|
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
|
||||||
o.Expression {
|
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([
|
return o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
|
||||||
o.literalArr(playerExprs)
|
o.literalArr(playerExprs)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
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([
|
return o.importExpr(resolveIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
|
||||||
o.literalArr(playerExprs)
|
o.literalArr(playerExprs)
|
||||||
]);
|
]);
|
||||||
@ -134,7 +141,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
|
|
||||||
visitAnimationStateDeclaration(
|
visitAnimationStateDeclaration(
|
||||||
ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
|
ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
|
||||||
var flatStyles: {[key: string]: string | number} = {};
|
const flatStyles: {[key: string]: string | number} = {};
|
||||||
_getStylesArray(ast).forEach(
|
_getStylesArray(ast).forEach(
|
||||||
entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); });
|
entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); });
|
||||||
context.stateMap.registerState(ast.stateName, flatStyles);
|
context.stateMap.registerState(ast.stateName, flatStyles);
|
||||||
@ -142,16 +149,17 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
|
|
||||||
visitAnimationStateTransition(
|
visitAnimationStateTransition(
|
||||||
ast: AnimationStateTransitionAst, context: _AnimationBuilderContext): any {
|
ast: AnimationStateTransitionAst, context: _AnimationBuilderContext): any {
|
||||||
var steps = ast.animation.steps;
|
const steps = ast.animation.steps;
|
||||||
var lastStep = steps[steps.length - 1];
|
const lastStep = steps[steps.length - 1];
|
||||||
if (_isEndStateAnimateStep(lastStep)) {
|
if (_isEndStateAnimateStep(lastStep)) {
|
||||||
context.endStateAnimateStep = <AnimationStepAst>lastStep;
|
context.endStateAnimateStep = <AnimationStepAst>lastStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.totalTransitionTime = 0;
|
context.totalTransitionTime = 0;
|
||||||
context.isExpectingFirstStyleStep = true;
|
context.isExpectingFirstStyleStep = true;
|
||||||
|
context.isExpectingFirstAnimateStep = true;
|
||||||
|
|
||||||
var stateChangePreconditions: o.Expression[] = [];
|
const stateChangePreconditions: o.Expression[] = [];
|
||||||
|
|
||||||
ast.stateChanges.forEach(stateChange => {
|
ast.stateChanges.forEach(stateChange => {
|
||||||
stateChangePreconditions.push(
|
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));
|
const reducedStateChangesPrecondition = stateChangePreconditions.reduce((a, b) => a.or(b));
|
||||||
var precondition =
|
const precondition =
|
||||||
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR).and(reducedStateChangesPrecondition);
|
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR).and(reducedStateChangesPrecondition);
|
||||||
|
|
||||||
var animationStmt = _ANIMATION_PLAYER_VAR.set(animationPlayerExpr).toStmt();
|
const animationStmt = _ANIMATION_PLAYER_VAR.set(animationPlayerExpr).toStmt();
|
||||||
var totalTimeStmt = _ANIMATION_TIME_VAR.set(o.literal(context.totalTransitionTime)).toStmt();
|
const totalTimeStmt = _ANIMATION_TIME_VAR.set(o.literal(context.totalTransitionTime)).toStmt();
|
||||||
|
|
||||||
return new o.IfStmt(precondition, [animationStmt, totalTimeStmt]);
|
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
|
// this should always be defined even if the user overrides it
|
||||||
context.stateMap.registerState(DEFAULT_STATE, {});
|
context.stateMap.registerState(DEFAULT_STATE, {});
|
||||||
|
|
||||||
var statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
|
statements.push(_PREVIOUS_ANIMATION_PLAYERS
|
||||||
.callMethod(
|
.set(_ANIMATION_FACTORY_VIEW_CONTEXT.callMethod(
|
||||||
'cancelActiveAnimation',
|
'getAnimationPlayers',
|
||||||
[
|
[
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
||||||
_ANIMATION_NEXT_STATE_VAR.equals(o.literal(EMPTY_STATE))
|
_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_PLAYER_VAR.set(o.NULL_EXPR).toDeclStmt());
|
||||||
statements.push(_ANIMATION_TIME_VAR.set(o.literal(0)).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.equals(o.NULL_EXPR),
|
||||||
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
|
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
|
||||||
|
|
||||||
var RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles));
|
const RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles));
|
||||||
|
|
||||||
// before we start any animation we want to clear out the starting
|
|
||||||
// styles from the element's style property (since they were placed
|
|
||||||
// there at the end of the last animation
|
|
||||||
statements.push(RENDER_STYLES_FN
|
|
||||||
.callFn([
|
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
|
||||||
o.importExpr(resolveIdentifier(Identifiers.clearStyles))
|
|
||||||
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
|
||||||
])
|
|
||||||
.toStmt());
|
|
||||||
|
|
||||||
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
||||||
|
|
||||||
@ -251,19 +247,40 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
_ANIMATION_PLAYER_VAR
|
_ANIMATION_PLAYER_VAR
|
||||||
.callMethod(
|
.callMethod(
|
||||||
'onDone',
|
'onDone',
|
||||||
[o.fn(
|
[o
|
||||||
[],
|
.fn([],
|
||||||
[RENDER_STYLES_FN
|
[
|
||||||
.callFn([
|
_ANIMATION_PLAYER_VAR.callMethod('destroy', []).toStmt(),
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
RENDER_STYLES_FN
|
||||||
o.importExpr(resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
|
|
||||||
.callFn([
|
.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());
|
.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
|
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
|
||||||
.callMethod(
|
.callMethod(
|
||||||
'queueAnimation',
|
'queueAnimation',
|
||||||
@ -292,16 +309,16 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build(ast: AnimationAst): AnimationEntryCompileResult {
|
build(ast: AnimationAst): AnimationEntryCompileResult {
|
||||||
var context = new _AnimationBuilderContext();
|
const context = new _AnimationBuilderContext();
|
||||||
var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
|
const fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
|
||||||
var fnVariable = o.variable(this._fnVarName);
|
const fnVariable = o.variable(this._fnVarName);
|
||||||
|
|
||||||
var lookupMap: any[] = [];
|
const lookupMap: any[] = [];
|
||||||
Object.keys(context.stateMap.states).forEach(stateName => {
|
Object.keys(context.stateMap.states).forEach(stateName => {
|
||||||
const value = context.stateMap.states[stateName];
|
const value = context.stateMap.states[stateName];
|
||||||
var variableValue = EMPTY_MAP;
|
let variableValue = _EMPTY_MAP;
|
||||||
if (isPresent(value)) {
|
if (isPresent(value)) {
|
||||||
let styleMap: any[] = [];
|
const styleMap: any[] = [];
|
||||||
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
|
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
|
||||||
variableValue = o.literalMap(styleMap);
|
variableValue = o.literalMap(styleMap);
|
||||||
}
|
}
|
||||||
@ -319,6 +336,7 @@ class _AnimationBuilderContext {
|
|||||||
stateMap = new _AnimationBuilderStateMap();
|
stateMap = new _AnimationBuilderStateMap();
|
||||||
endStateAnimateStep: AnimationStepAst = null;
|
endStateAnimateStep: AnimationStepAst = null;
|
||||||
isExpectingFirstStyleStep = false;
|
isExpectingFirstStyleStep = false;
|
||||||
|
isExpectingFirstAnimateStep = false;
|
||||||
totalTransitionTime = 0;
|
totalTransitionTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +344,7 @@ class _AnimationBuilderStateMap {
|
|||||||
private _states: {[key: string]: {[prop: string]: string | number}} = {};
|
private _states: {[key: string]: {[prop: string]: string | number}} = {};
|
||||||
get states() { return this._states; }
|
get states() { return this._states; }
|
||||||
registerState(name: string, value: {[prop: string]: string | number} = null): void {
|
registerState(name: string, value: {[prop: string]: string | number} = null): void {
|
||||||
var existingEntry = this._states[name];
|
const existingEntry = this._states[name];
|
||||||
if (!existingEntry) {
|
if (!existingEntry) {
|
||||||
this._states[name] = value;
|
this._states[name] = value;
|
||||||
}
|
}
|
||||||
@ -334,7 +352,7 @@ class _AnimationBuilderStateMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _compareToAnimationStateExpr(value: o.Expression, animationState: string): o.Expression {
|
function _compareToAnimationStateExpr(value: o.Expression, animationState: string): o.Expression {
|
||||||
var emptyStateLiteral = o.literal(EMPTY_STATE);
|
const emptyStateLiteral = o.literal(EMPTY_STATE);
|
||||||
switch (animationState) {
|
switch (animationState) {
|
||||||
case EMPTY_STATE:
|
case EMPTY_STATE:
|
||||||
return value.equals(emptyStateLiteral);
|
return value.equals(emptyStateLiteral);
|
||||||
@ -351,8 +369,8 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean {
|
|||||||
// the final animation step is characterized by having only TWO
|
// the final animation step is characterized by having only TWO
|
||||||
// keyframe values and it must have zero styles for both keyframes
|
// keyframe values and it must have zero styles for both keyframes
|
||||||
if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) {
|
if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) {
|
||||||
var styles1 = _getStylesArray(step.keyframes[0])[0];
|
const styles1 = _getStylesArray(step.keyframes[0])[0];
|
||||||
var styles2 = _getStylesArray(step.keyframes[1])[0];
|
const styles2 = _getStylesArray(step.keyframes[1])[0];
|
||||||
return Object.keys(styles1).length === 0 && Object.keys(styles2).length === 0;
|
return Object.keys(styles1).length === 0 && Object.keys(styles2).length === 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -6,11 +6,14 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||||
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
|
||||||
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
||||||
import {StylesCollection} from './styles_collection';
|
import {StylesCollection} from './styles_collection';
|
||||||
@ -32,7 +35,10 @@ export class AnimationEntryParseResult {
|
|||||||
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class AnimationParser {
|
export class AnimationParser {
|
||||||
|
constructor(private _schema: ElementSchemaRegistry) {}
|
||||||
|
|
||||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
const componentName = component.type.name;
|
const componentName = component.type.name;
|
||||||
@ -66,14 +72,14 @@ export class AnimationParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult {
|
parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult {
|
||||||
var errors: AnimationParseError[] = [];
|
const errors: AnimationParseError[] = [];
|
||||||
var stateStyles: {[key: string]: AnimationStylesAst} = {};
|
const stateStyles: {[key: string]: AnimationStylesAst} = {};
|
||||||
var transitions: CompileAnimationStateTransitionMetadata[] = [];
|
const transitions: CompileAnimationStateTransitionMetadata[] = [];
|
||||||
|
|
||||||
var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
const stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
||||||
entry.definitions.forEach(def => {
|
entry.definitions.forEach(def => {
|
||||||
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
||||||
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
|
_parseAnimationDeclarationStates(def, this._schema, errors).forEach(ast => {
|
||||||
stateDeclarationAsts.push(ast);
|
stateDeclarationAsts.push(ast);
|
||||||
stateStyles[ast.stateName] = ast.styles;
|
stateStyles[ast.stateName] = ast.styles;
|
||||||
});
|
});
|
||||||
@ -82,50 +88,40 @@ export class AnimationParser {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var stateTransitionAsts =
|
const stateTransitionAsts = transitions.map(
|
||||||
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
|
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);
|
return new AnimationEntryParseResult(ast, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseAnimationDeclarationStates(
|
function _parseAnimationDeclarationStates(
|
||||||
stateMetadata: CompileAnimationStateDeclarationMetadata,
|
stateMetadata: CompileAnimationStateDeclarationMetadata, schema: ElementSchemaRegistry,
|
||||||
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
||||||
var styleValues: Styles[] = [];
|
const normalizedStyles = _normalizeStyleMetadata(stateMetadata.styles, {}, schema, errors, false);
|
||||||
stateMetadata.styles.styles.forEach(stylesEntry => {
|
const defStyles = new AnimationStylesAst(normalizedStyles);
|
||||||
// TODO (matsko): change this when we get CSS class integration support
|
const states = stateMetadata.stateNameExpr.split(/\s*,\s*/);
|
||||||
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*/);
|
|
||||||
return states.map(state => new AnimationStateDeclarationAst(state, defStyles));
|
return states.map(state => new AnimationStateDeclarationAst(state, defStyles));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseAnimationStateTransition(
|
function _parseAnimationStateTransition(
|
||||||
transitionStateMetadata: CompileAnimationStateTransitionMetadata,
|
transitionStateMetadata: CompileAnimationStateTransitionMetadata,
|
||||||
stateStyles: {[key: string]: AnimationStylesAst},
|
stateStyles: {[key: string]: AnimationStylesAst}, schema: ElementSchemaRegistry,
|
||||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||||
var styles = new StylesCollection();
|
const styles = new StylesCollection();
|
||||||
var transitionExprs: AnimationStateTransitionExpression[] = [];
|
const transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||||
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
const transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
||||||
transitionStates.forEach(
|
transitionStates.forEach(
|
||||||
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
||||||
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
const entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||||
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
const animation = _normalizeStyleSteps(entry, stateStyles, schema, errors);
|
||||||
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
const animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||||
if (errors.length == 0) {
|
if (errors.length == 0) {
|
||||||
_fillAnimationAstStartingKeyframes(animationAst, styles, errors);
|
_fillAnimationAstStartingKeyframes(animationAst, styles, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
var stepsAst: AnimationWithStepsAst = (animationAst instanceof AnimationWithStepsAst) ?
|
const stepsAst: AnimationWithStepsAst = (animationAst instanceof AnimationWithStepsAst) ?
|
||||||
animationAst :
|
animationAst :
|
||||||
new AnimationSequenceAst([animationAst]);
|
new AnimationSequenceAst([animationAst]);
|
||||||
|
|
||||||
@ -147,22 +143,22 @@ function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): str
|
|||||||
|
|
||||||
function _parseAnimationTransitionExpr(
|
function _parseAnimationTransitionExpr(
|
||||||
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
||||||
var expressions: AnimationStateTransitionExpression[] = [];
|
const expressions: AnimationStateTransitionExpression[] = [];
|
||||||
if (eventStr[0] == ':') {
|
if (eventStr[0] == ':') {
|
||||||
eventStr = _parseAnimationAlias(eventStr, errors);
|
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) {
|
if (!isPresent(match) || match.length < 4) {
|
||||||
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
||||||
return expressions;
|
return expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fromState = match[1];
|
const fromState = match[1];
|
||||||
var separator = match[2];
|
const separator = match[2];
|
||||||
var toState = match[3];
|
const toState = match[3];
|
||||||
expressions.push(new AnimationStateTransitionExpression(fromState, toState));
|
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) {
|
if (separator[0] == '<' && !isFullAnyStateExpr) {
|
||||||
expressions.push(new AnimationStateTransitionExpression(toState, fromState));
|
expressions.push(new AnimationStateTransitionExpression(toState, fromState));
|
||||||
}
|
}
|
||||||
@ -176,13 +172,31 @@ function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnima
|
|||||||
|
|
||||||
function _normalizeStyleMetadata(
|
function _normalizeStyleMetadata(
|
||||||
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
schema: ElementSchemaRegistry, errors: AnimationParseError[],
|
||||||
var normalizedStyles: {[key: string]: string | number}[] = [];
|
permitStateReferences: boolean): {[key: string]: string | number}[] {
|
||||||
|
const normalizedStyles: {[key: string]: string | number}[] = [];
|
||||||
entry.styles.forEach(styleEntry => {
|
entry.styles.forEach(styleEntry => {
|
||||||
if (typeof styleEntry === 'string') {
|
if (typeof styleEntry === 'string') {
|
||||||
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 {
|
} 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;
|
return normalizedStyles;
|
||||||
@ -190,8 +204,8 @@ function _normalizeStyleMetadata(
|
|||||||
|
|
||||||
function _normalizeStyleSteps(
|
function _normalizeStyleSteps(
|
||||||
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): CompileAnimationMetadata {
|
schema: ElementSchemaRegistry, errors: AnimationParseError[]): CompileAnimationMetadata {
|
||||||
var steps = _normalizeStyleStepEntry(entry, stateStyles, errors);
|
const steps = _normalizeStyleStepEntry(entry, stateStyles, schema, errors);
|
||||||
return (entry instanceof CompileAnimationGroupMetadata) ?
|
return (entry instanceof CompileAnimationGroupMetadata) ?
|
||||||
new CompileAnimationGroupMetadata(steps) :
|
new CompileAnimationGroupMetadata(steps) :
|
||||||
new CompileAnimationSequenceMetadata(steps);
|
new CompileAnimationSequenceMetadata(steps);
|
||||||
@ -200,8 +214,8 @@ function _normalizeStyleSteps(
|
|||||||
function _mergeAnimationStyles(
|
function _mergeAnimationStyles(
|
||||||
stylesList: any[], newItem: {[key: string]: string | number} | string) {
|
stylesList: any[], newItem: {[key: string]: string | number} | string) {
|
||||||
if (typeof newItem === 'object' && newItem !== null && stylesList.length > 0) {
|
if (typeof newItem === 'object' && newItem !== null && stylesList.length > 0) {
|
||||||
var lastIndex = stylesList.length - 1;
|
const lastIndex = stylesList.length - 1;
|
||||||
var lastItem = stylesList[lastIndex];
|
const lastItem = stylesList[lastIndex];
|
||||||
if (typeof lastItem === 'object' && lastItem !== null) {
|
if (typeof lastItem === 'object' && lastItem !== null) {
|
||||||
stylesList[lastIndex] = StringMapWrapper.merge(
|
stylesList[lastIndex] = StringMapWrapper.merge(
|
||||||
<{[key: string]: string | number}>lastItem, <{[key: string]: string | number}>newItem);
|
<{[key: string]: string | number}>lastItem, <{[key: string]: string | number}>newItem);
|
||||||
@ -213,16 +227,16 @@ function _mergeAnimationStyles(
|
|||||||
|
|
||||||
function _normalizeStyleStepEntry(
|
function _normalizeStyleStepEntry(
|
||||||
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
schema: ElementSchemaRegistry, errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
||||||
var steps: CompileAnimationMetadata[];
|
let steps: CompileAnimationMetadata[];
|
||||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||||
steps = entry.steps;
|
steps = entry.steps;
|
||||||
} else {
|
} else {
|
||||||
return [entry];
|
return [entry];
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSteps: CompileAnimationMetadata[] = [];
|
const newSteps: CompileAnimationMetadata[] = [];
|
||||||
var combinedStyles: Styles[];
|
let combinedStyles: Styles[];
|
||||||
steps.forEach(step => {
|
steps.forEach(step => {
|
||||||
if (step instanceof CompileAnimationStyleMetadata) {
|
if (step instanceof CompileAnimationStyleMetadata) {
|
||||||
// this occurs when a style step is followed by a previous style step
|
// this occurs when a style step is followed by a previous style step
|
||||||
@ -232,7 +246,8 @@ function _normalizeStyleStepEntry(
|
|||||||
if (!isPresent(combinedStyles)) {
|
if (!isPresent(combinedStyles)) {
|
||||||
combinedStyles = [];
|
combinedStyles = [];
|
||||||
}
|
}
|
||||||
_normalizeStyleMetadata(<CompileAnimationStyleMetadata>step, stateStyles, errors)
|
_normalizeStyleMetadata(
|
||||||
|
<CompileAnimationStyleMetadata>step, stateStyles, schema, errors, true)
|
||||||
.forEach(entry => { _mergeAnimationStyles(combinedStyles, entry); });
|
.forEach(entry => { _mergeAnimationStyles(combinedStyles, entry); });
|
||||||
} else {
|
} else {
|
||||||
// it is important that we create a metadata entry of the combined styles
|
// it is important that we create a metadata entry of the combined styles
|
||||||
@ -247,16 +262,17 @@ function _normalizeStyleStepEntry(
|
|||||||
if (step instanceof CompileAnimationAnimateMetadata) {
|
if (step instanceof CompileAnimationAnimateMetadata) {
|
||||||
// we do not recurse into CompileAnimationAnimateMetadata since
|
// we do not recurse into CompileAnimationAnimateMetadata since
|
||||||
// those style steps are not going to be squashed
|
// those style steps are not going to be squashed
|
||||||
var animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
const animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
||||||
if (animateStyleValue instanceof CompileAnimationStyleMetadata) {
|
if (animateStyleValue instanceof CompileAnimationStyleMetadata) {
|
||||||
animateStyleValue.styles =
|
animateStyleValue.styles =
|
||||||
_normalizeStyleMetadata(animateStyleValue, stateStyles, errors);
|
_normalizeStyleMetadata(animateStyleValue, stateStyles, schema, errors, true);
|
||||||
} else if (animateStyleValue instanceof CompileAnimationKeyframesSequenceMetadata) {
|
} else if (animateStyleValue instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||||
animateStyleValue.steps.forEach(
|
animateStyleValue.steps.forEach(step => {
|
||||||
step => { step.styles = _normalizeStyleMetadata(step, stateStyles, errors); });
|
step.styles = _normalizeStyleMetadata(step, stateStyles, schema, errors, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (step instanceof CompileAnimationWithStepsMetadata) {
|
} else if (step instanceof CompileAnimationWithStepsMetadata) {
|
||||||
let innerSteps = _normalizeStyleStepEntry(step, stateStyles, errors);
|
const innerSteps = _normalizeStyleStepEntry(step, stateStyles, schema, errors);
|
||||||
step = step instanceof CompileAnimationGroupMetadata ?
|
step = step instanceof CompileAnimationGroupMetadata ?
|
||||||
new CompileAnimationGroupMetadata(innerSteps) :
|
new CompileAnimationGroupMetadata(innerSteps) :
|
||||||
new CompileAnimationSequenceMetadata(innerSteps);
|
new CompileAnimationSequenceMetadata(innerSteps);
|
||||||
@ -278,12 +294,12 @@ function _normalizeStyleStepEntry(
|
|||||||
function _resolveStylesFromState(
|
function _resolveStylesFromState(
|
||||||
stateName: string, stateStyles: {[key: string]: AnimationStylesAst},
|
stateName: string, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]) {
|
errors: AnimationParseError[]) {
|
||||||
var styles: Styles[] = [];
|
const styles: Styles[] = [];
|
||||||
if (stateName[0] != ':') {
|
if (stateName[0] != ':') {
|
||||||
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
|
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
|
||||||
} else {
|
} else {
|
||||||
var normalizedStateName = stateName.substring(1);
|
const normalizedStateName = stateName.substring(1);
|
||||||
var value = stateStyles[normalizedStateName];
|
const value = stateStyles[normalizedStateName];
|
||||||
if (!isPresent(value)) {
|
if (!isPresent(value)) {
|
||||||
errors.push(new AnimationParseError(
|
errors.push(new AnimationParseError(
|
||||||
`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
|
`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
|
||||||
@ -306,8 +322,8 @@ function _parseAnimationKeyframes(
|
|||||||
keyframeSequence: CompileAnimationKeyframesSequenceMetadata, currentTime: number,
|
keyframeSequence: CompileAnimationKeyframesSequenceMetadata, currentTime: number,
|
||||||
collectedStyles: StylesCollection, stateStyles: {[key: string]: AnimationStylesAst},
|
collectedStyles: StylesCollection, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): AnimationKeyframeAst[] {
|
errors: AnimationParseError[]): AnimationKeyframeAst[] {
|
||||||
var totalEntries = keyframeSequence.steps.length;
|
const totalEntries = keyframeSequence.steps.length;
|
||||||
var totalOffsets = 0;
|
let totalOffsets = 0;
|
||||||
keyframeSequence.steps.forEach(step => totalOffsets += (isPresent(step.offset) ? 1 : 0));
|
keyframeSequence.steps.forEach(step => totalOffsets += (isPresent(step.offset) ? 1 : 0));
|
||||||
|
|
||||||
if (totalOffsets > 0 && totalOffsets < totalEntries) {
|
if (totalOffsets > 0 && totalOffsets < totalEntries) {
|
||||||
@ -316,15 +332,15 @@ function _parseAnimationKeyframes(
|
|||||||
totalOffsets = totalEntries;
|
totalOffsets = totalEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
var limit = totalEntries - 1;
|
let limit = totalEntries - 1;
|
||||||
var margin = totalOffsets == 0 ? (1 / limit) : 0;
|
const margin = totalOffsets == 0 ? (1 / limit) : 0;
|
||||||
var rawKeyframes: any[] /** TODO #9100 */ = [];
|
const rawKeyframes: any[] /** TODO #9100 */ = [];
|
||||||
var index = 0;
|
let index = 0;
|
||||||
var doSortKeyframes = false;
|
let doSortKeyframes = false;
|
||||||
var lastOffset = 0;
|
let lastOffset = 0;
|
||||||
keyframeSequence.steps.forEach(styleMetadata => {
|
keyframeSequence.steps.forEach(styleMetadata => {
|
||||||
var offset = styleMetadata.offset;
|
let offset = styleMetadata.offset;
|
||||||
var keyframeStyles: Styles = {};
|
const keyframeStyles: Styles = {};
|
||||||
styleMetadata.styles.forEach(entry => {
|
styleMetadata.styles.forEach(entry => {
|
||||||
Object.keys(entry).forEach(prop => {
|
Object.keys(entry).forEach(prop => {
|
||||||
if (prop != 'offset') {
|
if (prop != 'offset') {
|
||||||
@ -348,23 +364,23 @@ function _parseAnimationKeyframes(
|
|||||||
rawKeyframes.sort((a, b) => a[0] <= b[0] ? -1 : 1);
|
rawKeyframes.sort((a, b) => a[0] <= b[0] ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstKeyframe = rawKeyframes[0];
|
let firstKeyframe = rawKeyframes[0];
|
||||||
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
||||||
rawKeyframes.splice(0, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
rawKeyframes.splice(0, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstKeyframeStyles = firstKeyframe[1];
|
const firstKeyframeStyles = firstKeyframe[1];
|
||||||
limit = rawKeyframes.length - 1;
|
limit = rawKeyframes.length - 1;
|
||||||
var lastKeyframe = rawKeyframes[limit];
|
let lastKeyframe = rawKeyframes[limit];
|
||||||
if (lastKeyframe[0] != _TERMINAL_KEYFRAME) {
|
if (lastKeyframe[0] != _TERMINAL_KEYFRAME) {
|
||||||
rawKeyframes.push(lastKeyframe = [_TERMINAL_KEYFRAME, {}]);
|
rawKeyframes.push(lastKeyframe = [_TERMINAL_KEYFRAME, {}]);
|
||||||
limit++;
|
limit++;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastKeyframeStyles = lastKeyframe[1];
|
const lastKeyframeStyles = lastKeyframe[1];
|
||||||
for (let i = 1; i <= limit; i++) {
|
for (let i = 1; i <= limit; i++) {
|
||||||
let entry = rawKeyframes[i];
|
const entry = rawKeyframes[i];
|
||||||
let styles = entry[1];
|
const styles = entry[1];
|
||||||
|
|
||||||
Object.keys(styles).forEach(prop => {
|
Object.keys(styles).forEach(prop => {
|
||||||
if (!isPresent(firstKeyframeStyles[prop])) {
|
if (!isPresent(firstKeyframeStyles[prop])) {
|
||||||
@ -374,8 +390,8 @@ function _parseAnimationKeyframes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = limit - 1; i >= 0; i--) {
|
for (let i = limit - 1; i >= 0; i--) {
|
||||||
let entry = rawKeyframes[i];
|
const entry = rawKeyframes[i];
|
||||||
let styles = entry[1];
|
const styles = entry[1];
|
||||||
|
|
||||||
Object.keys(styles).forEach(prop => {
|
Object.keys(styles).forEach(prop => {
|
||||||
if (!isPresent(lastKeyframeStyles[prop])) {
|
if (!isPresent(lastKeyframeStyles[prop])) {
|
||||||
@ -391,21 +407,21 @@ function _parseAnimationKeyframes(
|
|||||||
function _parseTransitionAnimation(
|
function _parseTransitionAnimation(
|
||||||
entry: CompileAnimationMetadata, currentTime: number, collectedStyles: StylesCollection,
|
entry: CompileAnimationMetadata, currentTime: number, collectedStyles: StylesCollection,
|
||||||
stateStyles: {[key: string]: AnimationStylesAst}, errors: AnimationParseError[]): AnimationAst {
|
stateStyles: {[key: string]: AnimationStylesAst}, errors: AnimationParseError[]): AnimationAst {
|
||||||
var ast: any /** TODO #9100 */;
|
let ast: any /** TODO #9100 */;
|
||||||
var playTime = 0;
|
let playTime = 0;
|
||||||
var startingTime = currentTime;
|
const startingTime = currentTime;
|
||||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||||
var maxDuration = 0;
|
let maxDuration = 0;
|
||||||
var steps: any[] /** TODO #9100 */ = [];
|
const steps: any[] /** TODO #9100 */ = [];
|
||||||
var isGroup = entry instanceof CompileAnimationGroupMetadata;
|
const isGroup = entry instanceof CompileAnimationGroupMetadata;
|
||||||
var previousStyles: any /** TODO #9100 */;
|
let previousStyles: any /** TODO #9100 */;
|
||||||
entry.steps.forEach(entry => {
|
entry.steps.forEach(entry => {
|
||||||
// these will get picked up by the next step...
|
// these will get picked up by the next step...
|
||||||
var time = isGroup ? startingTime : currentTime;
|
const time = isGroup ? startingTime : currentTime;
|
||||||
if (entry instanceof CompileAnimationStyleMetadata) {
|
if (entry instanceof CompileAnimationStyleMetadata) {
|
||||||
entry.styles.forEach(stylesEntry => {
|
entry.styles.forEach(stylesEntry => {
|
||||||
// by this point we know that we only have stringmap values
|
// 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(
|
Object.keys(map).forEach(
|
||||||
prop => { collectedStyles.insertAtTime(prop, time, map[prop]); });
|
prop => { collectedStyles.insertAtTime(prop, time, map[prop]); });
|
||||||
});
|
});
|
||||||
@ -413,26 +429,26 @@ function _parseTransitionAnimation(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerAst = _parseTransitionAnimation(entry, time, collectedStyles, stateStyles, errors);
|
const innerAst = _parseTransitionAnimation(entry, time, collectedStyles, stateStyles, errors);
|
||||||
if (isPresent(previousStyles)) {
|
if (isPresent(previousStyles)) {
|
||||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||||
let startingStyles = new AnimationStylesAst(previousStyles);
|
const startingStyles = new AnimationStylesAst(previousStyles);
|
||||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||||
} else {
|
} else {
|
||||||
var innerStep = <AnimationStepAst>innerAst;
|
const innerStep = <AnimationStepAst>innerAst;
|
||||||
innerStep.startingStyles.styles.push(...previousStyles);
|
innerStep.startingStyles.styles.push(...previousStyles);
|
||||||
}
|
}
|
||||||
previousStyles = null;
|
previousStyles = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var astDuration = innerAst.playTime;
|
const astDuration = innerAst.playTime;
|
||||||
currentTime += astDuration;
|
currentTime += astDuration;
|
||||||
playTime += astDuration;
|
playTime += astDuration;
|
||||||
maxDuration = Math.max(astDuration, maxDuration);
|
maxDuration = Math.max(astDuration, maxDuration);
|
||||||
steps.push(innerAst);
|
steps.push(innerAst);
|
||||||
});
|
});
|
||||||
if (isPresent(previousStyles)) {
|
if (isPresent(previousStyles)) {
|
||||||
let startingStyles = new AnimationStylesAst(previousStyles);
|
const startingStyles = new AnimationStylesAst(previousStyles);
|
||||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||||
}
|
}
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
@ -443,18 +459,18 @@ function _parseTransitionAnimation(
|
|||||||
ast = new AnimationSequenceAst(steps);
|
ast = new AnimationSequenceAst(steps);
|
||||||
}
|
}
|
||||||
} else if (entry instanceof CompileAnimationAnimateMetadata) {
|
} else if (entry instanceof CompileAnimationAnimateMetadata) {
|
||||||
var timings = _parseTimeExpression(entry.timings, errors);
|
const timings = _parseTimeExpression(entry.timings, errors);
|
||||||
var styles = entry.styles;
|
const styles = entry.styles;
|
||||||
|
|
||||||
var keyframes: any /** TODO #9100 */;
|
let keyframes: any /** TODO #9100 */;
|
||||||
if (styles instanceof CompileAnimationKeyframesSequenceMetadata) {
|
if (styles instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||||
keyframes =
|
keyframes =
|
||||||
_parseAnimationKeyframes(styles, currentTime, collectedStyles, stateStyles, errors);
|
_parseAnimationKeyframes(styles, currentTime, collectedStyles, stateStyles, errors);
|
||||||
} else {
|
} else {
|
||||||
let styleData = <CompileAnimationStyleMetadata>styles;
|
const styleData = <CompileAnimationStyleMetadata>styles;
|
||||||
let offset = _TERMINAL_KEYFRAME;
|
const offset = _TERMINAL_KEYFRAME;
|
||||||
let styleAst = new AnimationStylesAst(styleData.styles as Styles[]);
|
const styleAst = new AnimationStylesAst(styleData.styles as Styles[]);
|
||||||
var keyframe = new AnimationKeyframeAst(offset, styleAst);
|
const keyframe = new AnimationKeyframeAst(offset, styleAst);
|
||||||
keyframes = [keyframe];
|
keyframes = [keyframe];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,10 +499,10 @@ function _fillAnimationAstStartingKeyframes(
|
|||||||
ast: AnimationAst, collectedStyles: StylesCollection, errors: AnimationParseError[]): void {
|
ast: AnimationAst, collectedStyles: StylesCollection, errors: AnimationParseError[]): void {
|
||||||
// steps that only contain style will not be filled
|
// steps that only contain style will not be filled
|
||||||
if ((ast instanceof AnimationStepAst) && ast.keyframes.length > 0) {
|
if ((ast instanceof AnimationStepAst) && ast.keyframes.length > 0) {
|
||||||
var keyframes = ast.keyframes;
|
const keyframes = ast.keyframes;
|
||||||
if (keyframes.length == 1) {
|
if (keyframes.length == 1) {
|
||||||
var endKeyframe = keyframes[0];
|
const endKeyframe = keyframes[0];
|
||||||
var startKeyframe = _createStartKeyframeFromEndKeyframe(
|
const startKeyframe = _createStartKeyframeFromEndKeyframe(
|
||||||
endKeyframe, ast.startTime, ast.playTime, collectedStyles, errors);
|
endKeyframe, ast.startTime, ast.playTime, collectedStyles, errors);
|
||||||
ast.keyframes = [startKeyframe, endKeyframe];
|
ast.keyframes = [startKeyframe, endKeyframe];
|
||||||
}
|
}
|
||||||
@ -497,10 +513,10 @@ function _fillAnimationAstStartingKeyframes(
|
|||||||
|
|
||||||
function _parseTimeExpression(
|
function _parseTimeExpression(
|
||||||
exp: string | number, errors: AnimationParseError[]): _AnimationTimings {
|
exp: string | number, errors: AnimationParseError[]): _AnimationTimings {
|
||||||
var regex = /^([\.\d]+)(m?s)(?:\s+([\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?/i;
|
const regex = /^([\.\d]+)(m?s)(?:\s+([\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?/i;
|
||||||
var duration: number;
|
let duration: number;
|
||||||
var delay: number = 0;
|
let delay: number = 0;
|
||||||
var easing: string = null;
|
let easing: string = null;
|
||||||
if (typeof exp === 'string') {
|
if (typeof exp === 'string') {
|
||||||
const matches = exp.match(regex);
|
const matches = exp.match(regex);
|
||||||
if (matches === null) {
|
if (matches === null) {
|
||||||
@ -508,24 +524,24 @@ function _parseTimeExpression(
|
|||||||
return new _AnimationTimings(0, 0, null);
|
return new _AnimationTimings(0, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var durationMatch = parseFloat(matches[1]);
|
let durationMatch = parseFloat(matches[1]);
|
||||||
var durationUnit = matches[2];
|
const durationUnit = matches[2];
|
||||||
if (durationUnit == 's') {
|
if (durationUnit == 's') {
|
||||||
durationMatch *= _ONE_SECOND;
|
durationMatch *= _ONE_SECOND;
|
||||||
}
|
}
|
||||||
duration = Math.floor(durationMatch);
|
duration = Math.floor(durationMatch);
|
||||||
|
|
||||||
var delayMatch = matches[3];
|
const delayMatch = matches[3];
|
||||||
var delayUnit = matches[4];
|
const delayUnit = matches[4];
|
||||||
if (isPresent(delayMatch)) {
|
if (isPresent(delayMatch)) {
|
||||||
var delayVal: number = parseFloat(delayMatch);
|
let delayVal: number = parseFloat(delayMatch);
|
||||||
if (isPresent(delayUnit) && delayUnit == 's') {
|
if (isPresent(delayUnit) && delayUnit == 's') {
|
||||||
delayVal *= _ONE_SECOND;
|
delayVal *= _ONE_SECOND;
|
||||||
}
|
}
|
||||||
delay = Math.floor(delayVal);
|
delay = Math.floor(delayVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
var easingVal = matches[5];
|
const easingVal = matches[5];
|
||||||
if (!isBlank(easingVal)) {
|
if (!isBlank(easingVal)) {
|
||||||
easing = easingVal;
|
easing = easingVal;
|
||||||
}
|
}
|
||||||
@ -539,15 +555,15 @@ function _parseTimeExpression(
|
|||||||
function _createStartKeyframeFromEndKeyframe(
|
function _createStartKeyframeFromEndKeyframe(
|
||||||
endKeyframe: AnimationKeyframeAst, startTime: number, duration: number,
|
endKeyframe: AnimationKeyframeAst, startTime: number, duration: number,
|
||||||
collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst {
|
collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst {
|
||||||
var values: Styles = {};
|
const values: Styles = {};
|
||||||
var endTime = startTime + duration;
|
const endTime = startTime + duration;
|
||||||
endKeyframe.styles.styles.forEach((styleData: Styles) => {
|
endKeyframe.styles.styles.forEach((styleData: Styles) => {
|
||||||
Object.keys(styleData).forEach(prop => {
|
Object.keys(styleData).forEach(prop => {
|
||||||
const val = styleData[prop];
|
const val = styleData[prop];
|
||||||
if (prop == 'offset') return;
|
if (prop == 'offset') return;
|
||||||
|
|
||||||
var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);
|
const resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);
|
||||||
var resultEntry: any /** TODO #9100 */, nextEntry: any /** TODO #9100 */,
|
let resultEntry: any /** TODO #9100 */, nextEntry: any /** TODO #9100 */,
|
||||||
value: any /** TODO #9100 */;
|
value: any /** TODO #9100 */;
|
||||||
if (isPresent(resultIndex)) {
|
if (isPresent(resultIndex)) {
|
||||||
resultEntry = collectedStyles.getByIndex(prop, resultIndex);
|
resultEntry = collectedStyles.getByIndex(prop, resultIndex);
|
||||||
|
@ -20,16 +20,16 @@ export class StylesCollection {
|
|||||||
styles: {[key: string]: StylesCollectionEntry[]} = {};
|
styles: {[key: string]: StylesCollectionEntry[]} = {};
|
||||||
|
|
||||||
insertAtTime(property: string, time: number, value: string|number) {
|
insertAtTime(property: string, time: number, value: string|number) {
|
||||||
var tuple = new StylesCollectionEntry(time, value);
|
const tuple = new StylesCollectionEntry(time, value);
|
||||||
var entries = this.styles[property];
|
let entries = this.styles[property];
|
||||||
if (!isPresent(entries)) {
|
if (!isPresent(entries)) {
|
||||||
entries = this.styles[property] = [];
|
entries = this.styles[property] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert this at the right stop in the array
|
// insert this at the right stop in the array
|
||||||
// this way we can keep it sorted
|
// this way we can keep it sorted
|
||||||
var insertionIndex = 0;
|
let insertionIndex = 0;
|
||||||
for (var i = entries.length - 1; i >= 0; i--) {
|
for (let i = entries.length - 1; i >= 0; i--) {
|
||||||
if (entries[i].time <= time) {
|
if (entries[i].time <= time) {
|
||||||
insertionIndex = i + 1;
|
insertionIndex = i + 1;
|
||||||
break;
|
break;
|
||||||
@ -40,7 +40,7 @@ export class StylesCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getByIndex(property: string, index: number): StylesCollectionEntry {
|
getByIndex(property: string, index: number): StylesCollectionEntry {
|
||||||
var items = this.styles[property];
|
const items = this.styles[property];
|
||||||
if (isPresent(items)) {
|
if (isPresent(items)) {
|
||||||
return index >= items.length ? null : items[index];
|
return index >= items.length ? null : items[index];
|
||||||
}
|
}
|
||||||
@ -48,9 +48,9 @@ export class StylesCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexOfAtOrBeforeTime(property: string, time: number): number {
|
indexOfAtOrBeforeTime(property: string, time: number): number {
|
||||||
var entries = this.styles[property];
|
const entries = this.styles[property];
|
||||||
if (isPresent(entries)) {
|
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;
|
if (entries[i].time <= time) return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export function assertArrayOfStrings(identifier: string, value: any) {
|
|||||||
if (!Array.isArray(value)) {
|
if (!Array.isArray(value)) {
|
||||||
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
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') {
|
if (typeof value[i] !== 'string') {
|
||||||
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
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 {isPresent} from './facade/lang';
|
||||||
import {LifecycleHooks} from './private_import_core';
|
import {LifecycleHooks} from './private_import_core';
|
||||||
import {CssSelector} from './selector';
|
import {CssSelector} from './selector';
|
||||||
@ -98,6 +98,15 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier
|
|||||||
get identifier(): CompileIdentifierMetadata { return this; }
|
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 {
|
export class CompileDiDependencyMetadata {
|
||||||
isAttribute: boolean;
|
isAttribute: boolean;
|
||||||
isSelf: boolean;
|
isSelf: boolean;
|
||||||
@ -105,33 +114,27 @@ export class CompileDiDependencyMetadata {
|
|||||||
isSkipSelf: boolean;
|
isSkipSelf: boolean;
|
||||||
isOptional: boolean;
|
isOptional: boolean;
|
||||||
isValue: boolean;
|
isValue: boolean;
|
||||||
query: CompileQueryMetadata;
|
|
||||||
viewQuery: CompileQueryMetadata;
|
|
||||||
token: CompileTokenMetadata;
|
token: CompileTokenMetadata;
|
||||||
value: any;
|
value: any;
|
||||||
|
|
||||||
constructor(
|
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: {
|
||||||
{isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, query, viewQuery, token,
|
isAttribute?: boolean,
|
||||||
value}: {
|
isSelf?: boolean,
|
||||||
isAttribute?: boolean,
|
isHost?: boolean,
|
||||||
isSelf?: boolean,
|
isSkipSelf?: boolean,
|
||||||
isHost?: boolean,
|
isOptional?: boolean,
|
||||||
isSkipSelf?: boolean,
|
isValue?: boolean,
|
||||||
isOptional?: boolean,
|
query?: CompileQueryMetadata,
|
||||||
isValue?: boolean,
|
viewQuery?: CompileQueryMetadata,
|
||||||
query?: CompileQueryMetadata,
|
token?: CompileTokenMetadata,
|
||||||
viewQuery?: CompileQueryMetadata,
|
value?: any
|
||||||
token?: CompileTokenMetadata,
|
} = {}) {
|
||||||
value?: any
|
|
||||||
} = {}) {
|
|
||||||
this.isAttribute = !!isAttribute;
|
this.isAttribute = !!isAttribute;
|
||||||
this.isSelf = !!isSelf;
|
this.isSelf = !!isSelf;
|
||||||
this.isHost = !!isHost;
|
this.isHost = !!isHost;
|
||||||
this.isSkipSelf = !!isSkipSelf;
|
this.isSkipSelf = !!isSkipSelf;
|
||||||
this.isOptional = !!isOptional;
|
this.isOptional = !!isOptional;
|
||||||
this.isValue = !!isValue;
|
this.isValue = !!isValue;
|
||||||
this.query = query;
|
|
||||||
this.viewQuery = viewQuery;
|
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.value = value;
|
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.
|
* Metadata regarding compilation of a template.
|
||||||
*/
|
*/
|
||||||
@ -309,6 +322,34 @@ export class CompileTemplateMetadata {
|
|||||||
}
|
}
|
||||||
this.interpolation = interpolation;
|
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[]>,
|
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileTypeMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
viewDirectives?: CompileTypeMetadata[],
|
|
||||||
viewPipes?: CompileTypeMetadata[],
|
|
||||||
template?: CompileTemplateMetadata
|
template?: CompileTemplateMetadata
|
||||||
} = {}): CompileDirectiveMetadata {
|
} = {}): CompileDirectiveMetadata {
|
||||||
var hostListeners: {[key: string]: string} = {};
|
const hostListeners: {[key: string]: string} = {};
|
||||||
var hostProperties: {[key: string]: string} = {};
|
const hostProperties: {[key: string]: string} = {};
|
||||||
var hostAttributes: {[key: string]: string} = {};
|
const hostAttributes: {[key: string]: string} = {};
|
||||||
if (isPresent(host)) {
|
if (isPresent(host)) {
|
||||||
Object.keys(host).forEach(key => {
|
Object.keys(host).forEach(key => {
|
||||||
const value = host[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)) {
|
if (isPresent(inputs)) {
|
||||||
inputs.forEach((bindConfig: string) => {
|
inputs.forEach((bindConfig: string) => {
|
||||||
// canonical syntax: `dirProp: elProp`
|
// canonical syntax: `dirProp: elProp`
|
||||||
// if there is no `:`, use 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];
|
inputsMap[parts[0]] = parts[1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var outputsMap: {[key: string]: string} = {};
|
const outputsMap: {[key: string]: string} = {};
|
||||||
if (isPresent(outputs)) {
|
if (isPresent(outputs)) {
|
||||||
outputs.forEach((bindConfig: string) => {
|
outputs.forEach((bindConfig: string) => {
|
||||||
// canonical syntax: `dirProp: elProp`
|
// canonical syntax: `dirProp: elProp`
|
||||||
// if there is no `:`, use 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];
|
outputsMap[parts[0]] = parts[1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -402,8 +441,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
viewProviders: CompileProviderMetadata[];
|
viewProviders: CompileProviderMetadata[];
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
viewQueries: CompileQueryMetadata[];
|
viewQueries: CompileQueryMetadata[];
|
||||||
// Note: Need to keep types here to prevent cycles!
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
entryComponents: CompileTypeMetadata[];
|
|
||||||
|
|
||||||
template: CompileTemplateMetadata;
|
template: CompileTemplateMetadata;
|
||||||
|
|
||||||
@ -427,9 +465,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileTypeMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
viewDirectives?: CompileTypeMetadata[],
|
|
||||||
viewPipes?: CompileTypeMetadata[],
|
|
||||||
template?: CompileTemplateMetadata,
|
template?: CompileTemplateMetadata,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -452,6 +488,27 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
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):
|
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
||||||
CompileDirectiveMetadata {
|
CompileDirectiveMetadata {
|
||||||
var template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||||
return CompileDirectiveMetadata.create({
|
return CompileDirectiveMetadata.create({
|
||||||
type: new CompileTypeMetadata({
|
type: new CompileTypeMetadata({
|
||||||
reference: Object,
|
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 {
|
export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
@ -505,24 +568,48 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
|||||||
this.pure = !!pure;
|
this.pure = !!pure;
|
||||||
}
|
}
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
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.
|
* Metadata regarding compilation of a module.
|
||||||
*/
|
*/
|
||||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
declaredDirectives: CompileDirectiveMetadata[];
|
declaredDirectives: CompileIdentifierMetadata[];
|
||||||
exportedDirectives: CompileDirectiveMetadata[];
|
exportedDirectives: CompileIdentifierMetadata[];
|
||||||
declaredPipes: CompilePipeMetadata[];
|
declaredPipes: CompileIdentifierMetadata[];
|
||||||
exportedPipes: CompilePipeMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
// Note: See CompileDirectiveMetadata.entryComponents why this has to be a type.
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
entryComponents: CompileTypeMetadata[];
|
bootstrapComponents: CompileIdentifierMetadata[];
|
||||||
bootstrapComponents: CompileTypeMetadata[];
|
|
||||||
providers: CompileProviderMetadata[];
|
providers: CompileProviderMetadata[];
|
||||||
|
|
||||||
importedModules: CompileNgModuleMetadata[];
|
importedModules: CompileNgModuleSummary[];
|
||||||
exportedModules: CompileNgModuleMetadata[];
|
exportedModules: CompileNgModuleSummary[];
|
||||||
schemas: SchemaMetadata[];
|
schemas: SchemaMetadata[];
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
@ -535,14 +622,14 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
providers?:
|
providers?:
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||||
declaredDirectives?: CompileDirectiveMetadata[],
|
declaredDirectives?: CompileIdentifierMetadata[],
|
||||||
exportedDirectives?: CompileDirectiveMetadata[],
|
exportedDirectives?: CompileIdentifierMetadata[],
|
||||||
declaredPipes?: CompilePipeMetadata[],
|
declaredPipes?: CompileIdentifierMetadata[],
|
||||||
exportedPipes?: CompilePipeMetadata[],
|
exportedPipes?: CompileIdentifierMetadata[],
|
||||||
entryComponents?: CompileTypeMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
bootstrapComponents?: CompileTypeMetadata[],
|
bootstrapComponents?: CompileIdentifierMetadata[],
|
||||||
importedModules?: CompileNgModuleMetadata[],
|
importedModules?: CompileNgModuleSummary[],
|
||||||
exportedModules?: CompileNgModuleMetadata[],
|
exportedModules?: CompileNgModuleSummary[],
|
||||||
transitiveModule?: TransitiveCompileNgModuleMetadata,
|
transitiveModule?: TransitiveCompileNgModuleMetadata,
|
||||||
schemas?: SchemaMetadata[],
|
schemas?: SchemaMetadata[],
|
||||||
id?: string
|
id?: string
|
||||||
@ -563,18 +650,54 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
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 {
|
export class TransitiveCompileNgModuleMetadata {
|
||||||
directivesSet = new Set<Type<any>>();
|
directivesSet = new Set<any>();
|
||||||
pipesSet = new Set<Type<any>>();
|
pipesSet = new Set<any>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public modules: CompileNgModuleMetadata[], public providers: CompileProviderMetadata[],
|
public modules: CompileNgModuleInjectorSummary[], public providers: CompileProviderMetadata[],
|
||||||
public entryComponents: CompileTypeMetadata[], public directives: CompileDirectiveMetadata[],
|
public entryComponents: CompileIdentifierMetadata[],
|
||||||
public pipes: CompilePipeMetadata[]) {
|
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
|
||||||
directives.forEach(dir => this.directivesSet.add(dir.type.reference));
|
public directiveLoaders: (() => Promise<void>)[]) {
|
||||||
pipes.forEach(pipe => this.pipesSet.add(pipe.type.reference));
|
directives.forEach(dir => this.directivesSet.add(dir.reference));
|
||||||
|
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +711,7 @@ export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifi
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return MapWrapper.values(map);
|
return Array.from(map.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
function _normalizeArray(obj: any[]): any[] {
|
function _normalizeArray(obj: any[]): any[] {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||||
|
|
||||||
|
import {AnimationParser} from './animation/animation_parser';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
@ -74,7 +75,8 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
|||||||
UrlResolver,
|
UrlResolver,
|
||||||
DirectiveResolver,
|
DirectiveResolver,
|
||||||
PipeResolver,
|
PipeResolver,
|
||||||
NgModuleResolver
|
NgModuleResolver,
|
||||||
|
AnimationParser
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@ -140,7 +142,7 @@ function _mergeOptions(optionsArr: CompilerOptions[]): CompilerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _lastDefined<T>(args: T[]): T {
|
function _lastDefined<T>(args: T[]): T {
|
||||||
for (var i = args.length - 1; i >= 0; i--) {
|
for (let i = args.length - 1; i >= 0; i--) {
|
||||||
if (args[i] !== undefined) {
|
if (args[i] !== undefined) {
|
||||||
return args[i];
|
return args[i];
|
||||||
}
|
}
|
||||||
@ -149,7 +151,7 @@ function _lastDefined<T>(args: T[]): T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _mergeArrays(parts: any[][]): any[] {
|
function _mergeArrays(parts: any[][]): any[] {
|
||||||
let result: any[] = [];
|
const result: any[] = [];
|
||||||
parts.forEach((part) => part && result.push(...part));
|
parts.forEach((part) => part && result.push(...part));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
|||||||
export function createCheckBindingStmt(
|
export function createCheckBindingStmt(
|
||||||
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||||
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
||||||
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
let condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
||||||
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||||
]);
|
]);
|
||||||
if (evalResult.forceUpdate) {
|
if (evalResult.forceUpdate) {
|
||||||
|
@ -58,7 +58,7 @@ export function convertPropertyBinding(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (visitor.needsValueUnwrapper) {
|
if (visitor.needsValueUnwrapper) {
|
||||||
var initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
|
const initValueUnwrapperStmt = VAL_UNWRAPPER_VAR.callMethod('reset', []).toStmt();
|
||||||
stmts.push(initValueUnwrapperStmt);
|
stmts.push(initValueUnwrapperStmt);
|
||||||
}
|
}
|
||||||
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
|
stmts.push(currValExpr.set(outputExpr).toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
@ -71,7 +71,7 @@ export function convertPropertyBinding(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ConvertActionBindingResult {
|
export class ConvertActionBindingResult {
|
||||||
constructor(public stmts: o.Statement[], public preventDefault: o.Expression) {}
|
constructor(public stmts: o.Statement[], public preventDefault: o.ReadVarExpr) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,14 +86,14 @@ export function convertActionBinding(
|
|||||||
}
|
}
|
||||||
const visitor =
|
const visitor =
|
||||||
new _AstToIrVisitor(builder, nameResolver, implicitReceiver, null, bindingId, true);
|
new _AstToIrVisitor(builder, nameResolver, implicitReceiver, null, bindingId, true);
|
||||||
let actionStmts: o.Statement[] = [];
|
const actionStmts: o.Statement[] = [];
|
||||||
flattenStatements(action.visit(visitor, _Mode.Statement), actionStmts);
|
flattenStatements(action.visit(visitor, _Mode.Statement), actionStmts);
|
||||||
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
||||||
var lastIndex = actionStmts.length - 1;
|
const lastIndex = actionStmts.length - 1;
|
||||||
var preventDefaultVar: o.ReadVarExpr = null;
|
let preventDefaultVar: o.ReadVarExpr = null;
|
||||||
if (lastIndex >= 0) {
|
if (lastIndex >= 0) {
|
||||||
var lastStatement = actionStmts[lastIndex];
|
const lastStatement = actionStmts[lastIndex];
|
||||||
var returnExpr = convertStmtIntoExpression(lastStatement);
|
const returnExpr = convertStmtIntoExpression(lastStatement);
|
||||||
if (returnExpr) {
|
if (returnExpr) {
|
||||||
// Note: We need to cast the result of the method call to dynamic,
|
// Note: We need to cast the result of the method call to dynamic,
|
||||||
// as it might be a void method!
|
// as it might be a void method!
|
||||||
@ -112,7 +112,7 @@ export function convertActionBinding(
|
|||||||
*/
|
*/
|
||||||
export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
|
export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.Statement[] {
|
||||||
const unwrapperStmts: o.Statement[] = [];
|
const unwrapperStmts: o.Statement[] = [];
|
||||||
var readVars = o.findReadVarNames(stmts);
|
const readVars = o.findReadVarNames(stmts);
|
||||||
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
||||||
unwrapperStmts.push(
|
unwrapperStmts.push(
|
||||||
VAL_UNWRAPPER_VAR
|
VAL_UNWRAPPER_VAR
|
||||||
@ -175,7 +175,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
private bindingId: string, private isAction: boolean) {}
|
private bindingId: string, private isAction: boolean) {}
|
||||||
|
|
||||||
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
||||||
var op: o.BinaryOperator;
|
let op: o.BinaryOperator;
|
||||||
switch (ast.operation) {
|
switch (ast.operation) {
|
||||||
case '+':
|
case '+':
|
||||||
op = o.BinaryOperator.Plus;
|
op = o.BinaryOperator.Plus;
|
||||||
@ -275,7 +275,12 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
args.push(this.visit(ast.expressions[i], _Mode.Expression));
|
args.push(this.visit(ast.expressions[i], _Mode.Expression));
|
||||||
}
|
}
|
||||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn(args);
|
|
||||||
|
return ast.expressions.length <= 9 ?
|
||||||
|
o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
||||||
|
o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([
|
||||||
|
args[0], o.literalArr(args.slice(1))
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||||
@ -298,7 +303,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||||
let parts: any[] = [];
|
const parts: any[] = [];
|
||||||
for (let i = 0; i < ast.keys.length; i++) {
|
for (let i = 0; i < ast.keys.length; i++) {
|
||||||
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
|
parts.push([ast.keys[i], this.visit(ast.values[i], _Mode.Expression)]);
|
||||||
}
|
}
|
||||||
@ -325,9 +330,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
} else {
|
} else {
|
||||||
const args = this.visitAll(ast.args, _Mode.Expression);
|
const args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
let result: any = null;
|
let result: any = null;
|
||||||
let receiver = this.visit(ast.receiver, _Mode.Expression);
|
const receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
var varExpr = this._getLocal(ast.name);
|
const varExpr = this._getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (isPresent(varExpr)) {
|
||||||
result = varExpr.callFn(args);
|
result = varExpr.callFn(args);
|
||||||
}
|
}
|
||||||
@ -349,7 +354,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
return this.convertSafeAccess(ast, leftMostSafe, mode);
|
return this.convertSafeAccess(ast, leftMostSafe, mode);
|
||||||
} else {
|
} else {
|
||||||
let result: any = null;
|
let result: any = null;
|
||||||
var receiver = this.visit(ast.receiver, _Mode.Expression);
|
const receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
result = this._getLocal(ast.name);
|
result = this._getLocal(ast.name);
|
||||||
}
|
}
|
||||||
@ -361,9 +366,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||||
let receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
const receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
var varExpr = this._getLocal(ast.name);
|
const varExpr = this._getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (isPresent(varExpr)) {
|
||||||
throw new Error('Cannot assign to a reference or variable!');
|
throw new Error('Cannot assign to a reference or variable!');
|
||||||
}
|
}
|
||||||
@ -575,11 +580,11 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
|
|||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
||||||
}
|
}
|
||||||
var proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
|
const proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
|
||||||
var proxyParams: o.FnParam[] = [];
|
const proxyParams: o.FnParam[] = [];
|
||||||
var proxyReturnEntries: o.Expression[] = [];
|
const proxyReturnEntries: o.Expression[] = [];
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
var paramName = `p${i}`;
|
const paramName = `p${i}`;
|
||||||
proxyParams.push(new o.FnParam(paramName));
|
proxyParams.push(new o.FnParam(paramName));
|
||||||
proxyReturnEntries.push(o.variable(paramName));
|
proxyReturnEntries.push(o.variable(paramName));
|
||||||
}
|
}
|
||||||
@ -600,7 +605,7 @@ function createCachedLiteralMap(
|
|||||||
const proxyParams: o.FnParam[] = [];
|
const proxyParams: o.FnParam[] = [];
|
||||||
const proxyReturnEntries: [string, o.Expression][] = [];
|
const proxyReturnEntries: [string, o.Expression][] = [];
|
||||||
const values: o.Expression[] = [];
|
const values: o.Expression[] = [];
|
||||||
for (var i = 0; i < entries.length; i++) {
|
for (let i = 0; i < entries.length; i++) {
|
||||||
const paramName = `p${i}`;
|
const paramName = `p${i}`;
|
||||||
proxyParams.push(new o.FnParam(paramName));
|
proxyParams.push(new o.FnParam(paramName));
|
||||||
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
||||||
|
@ -40,7 +40,7 @@ export function createPureProxy(
|
|||||||
fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
|
fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
|
||||||
builder: {fields: o.ClassField[], ctorStmts: {push: (stmt: o.Statement) => void}}) {
|
builder: {fields: o.ClassField[], ctorStmts: {push: (stmt: o.Statement) => void}}) {
|
||||||
builder.fields.push(new o.ClassField(pureProxyProp.name, null));
|
builder.fields.push(new o.ClassField(pureProxyProp.name, null));
|
||||||
var pureProxyId =
|
const pureProxyId =
|
||||||
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
||||||
if (!pureProxyId) {
|
if (!pureProxyId) {
|
||||||
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||||
|
@ -10,7 +10,8 @@ import {SecurityContext} from '@angular/core';
|
|||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {BoundElementPropertyAst, PropertyBindingType} from '../template_parser/template_ast';
|
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||||
|
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {createEnumExpression} from './identifier_util';
|
import {createEnumExpression} from './identifier_util';
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ export function writeToRenderer(
|
|||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Style:
|
case PropertyBindingType.Style:
|
||||||
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
let strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||||
if (isPresent(boundProp.unit)) {
|
if (isPresent(boundProp.unit)) {
|
||||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||||
}
|
}
|
||||||
@ -83,7 +84,63 @@ function sanitizedValue(
|
|||||||
if (!securityContextExpression) {
|
if (!securityContextExpression) {
|
||||||
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
||||||
}
|
}
|
||||||
let ctx = view.prop('viewUtils').prop('sanitizer');
|
const ctx = view.prop('viewUtils').prop('sanitizer');
|
||||||
let args = [securityContextExpression, renderValue];
|
const args = [securityContextExpression, renderValue];
|
||||||
return ctx.callMethod('sanitize', args);
|
return ctx.callMethod('sanitize', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function triggerAnimation(
|
||||||
|
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
||||||
|
eventListener: o.Expression, renderElement: o.Expression, renderValue: o.Expression,
|
||||||
|
lastRenderValue: o.Expression) {
|
||||||
|
const detachStmts: o.Statement[] = [];
|
||||||
|
const updateStmts: o.Statement[] = [];
|
||||||
|
|
||||||
|
const animationName = boundProp.name;
|
||||||
|
|
||||||
|
const animationFnExpr =
|
||||||
|
componentView.prop('componentType').prop('animations').key(o.literal(animationName));
|
||||||
|
|
||||||
|
// it's important to normalize the void value as `void` explicitly
|
||||||
|
// so that the styles data can be obtained from the stringmap
|
||||||
|
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
||||||
|
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
|
||||||
|
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
||||||
|
|
||||||
|
updateStmts.push(
|
||||||
|
animationTransitionVar
|
||||||
|
.set(animationFnExpr.callFn([
|
||||||
|
view, renderElement,
|
||||||
|
lastRenderValue.equals(unitializedValue).conditional(emptyStateValue, lastRenderValue),
|
||||||
|
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
||||||
|
]))
|
||||||
|
.toDeclStmt());
|
||||||
|
|
||||||
|
detachStmts.push(
|
||||||
|
animationTransitionVar
|
||||||
|
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
||||||
|
.toDeclStmt());
|
||||||
|
|
||||||
|
const registerStmts = [
|
||||||
|
animationTransitionVar
|
||||||
|
.callMethod(
|
||||||
|
'onStart',
|
||||||
|
[eventListener.callMethod(
|
||||||
|
o.BuiltinMethod.Bind,
|
||||||
|
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'start'))])])
|
||||||
|
.toStmt(),
|
||||||
|
animationTransitionVar
|
||||||
|
.callMethod(
|
||||||
|
'onDone',
|
||||||
|
[eventListener.callMethod(
|
||||||
|
o.BuiltinMethod.Bind,
|
||||||
|
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'done'))])])
|
||||||
|
.toStmt(),
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
updateStmts.push(...registerStmts);
|
||||||
|
detachStmts.push(...registerStmts);
|
||||||
|
|
||||||
|
return {updateStmts, detachStmts};
|
||||||
|
}
|
||||||
|
@ -96,7 +96,7 @@ export class CssBlockDefinitionRuleAst extends CssBlockRuleAst {
|
|||||||
location: ParseSourceSpan, public strValue: string, type: BlockType,
|
location: ParseSourceSpan, public strValue: string, type: BlockType,
|
||||||
public query: CssAtRulePredicateAst, block: CssBlockAst) {
|
public query: CssAtRulePredicateAst, block: CssBlockAst) {
|
||||||
super(location, type, block);
|
super(location, type, block);
|
||||||
var firstCssToken: CssToken = query.tokens[0];
|
const firstCssToken: CssToken = query.tokens[0];
|
||||||
this.name = new CssToken(
|
this.name = new CssToken(
|
||||||
firstCssToken.index, firstCssToken.column, firstCssToken.line, CssTokenType.Identifier,
|
firstCssToken.index, firstCssToken.column, firstCssToken.line, CssTokenType.Identifier,
|
||||||
this.strValue);
|
this.strValue);
|
||||||
@ -238,9 +238,9 @@ export class CssUnknownTokenListAst extends CssRuleAst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function mergeTokens(tokens: CssToken[], separator: string = ''): CssToken {
|
export function mergeTokens(tokens: CssToken[], separator: string = ''): CssToken {
|
||||||
var mainToken = tokens[0];
|
const mainToken = tokens[0];
|
||||||
var str = mainToken.strValue;
|
let str = mainToken.strValue;
|
||||||
for (var i = 1; i < tokens.length; i++) {
|
for (let i = 1; i < tokens.length; i++) {
|
||||||
str += separator + tokens[i].strValue;
|
str += separator + tokens[i].strValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,18 +54,18 @@ export function generateErrorMessage(
|
|||||||
|
|
||||||
export function findProblemCode(
|
export function findProblemCode(
|
||||||
input: string, errorValue: string, index: number, column: number): string {
|
input: string, errorValue: string, index: number, column: number): string {
|
||||||
var endOfProblemLine = index;
|
let endOfProblemLine = index;
|
||||||
var current = charCode(input, index);
|
let current = charCode(input, index);
|
||||||
while (current > 0 && !isNewline(current)) {
|
while (current > 0 && !isNewline(current)) {
|
||||||
current = charCode(input, ++endOfProblemLine);
|
current = charCode(input, ++endOfProblemLine);
|
||||||
}
|
}
|
||||||
var choppedString = input.substring(0, endOfProblemLine);
|
const choppedString = input.substring(0, endOfProblemLine);
|
||||||
var pointerPadding = '';
|
let pointerPadding = '';
|
||||||
for (var i = 0; i < column; i++) {
|
for (let i = 0; i < column; i++) {
|
||||||
pointerPadding += ' ';
|
pointerPadding += ' ';
|
||||||
}
|
}
|
||||||
var pointerString = '';
|
let pointerString = '';
|
||||||
for (var i = 0; i < errorValue.length; i++) {
|
for (let i = 0; i < errorValue.length; i++) {
|
||||||
pointerString += '^';
|
pointerString += '^';
|
||||||
}
|
}
|
||||||
return choppedString + '\n' + pointerPadding + pointerString + '\n';
|
return choppedString + '\n' + pointerPadding + pointerString + '\n';
|
||||||
@ -185,16 +185,16 @@ export class CssScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
consume(type: CssTokenType, value: string = null): LexedCssResult {
|
consume(type: CssTokenType, value: string = null): LexedCssResult {
|
||||||
var mode = this._currentMode;
|
const mode = this._currentMode;
|
||||||
|
|
||||||
this.setMode(_trackWhitespace(mode) ? CssLexerMode.ALL_TRACK_WS : CssLexerMode.ALL);
|
this.setMode(_trackWhitespace(mode) ? CssLexerMode.ALL_TRACK_WS : CssLexerMode.ALL);
|
||||||
|
|
||||||
var previousIndex = this.index;
|
const previousIndex = this.index;
|
||||||
var previousLine = this.line;
|
const previousLine = this.line;
|
||||||
var previousColumn = this.column;
|
const previousColumn = this.column;
|
||||||
|
|
||||||
var next: CssToken;
|
let next: CssToken;
|
||||||
var output = this.scan();
|
const output = this.scan();
|
||||||
if (isPresent(output)) {
|
if (isPresent(output)) {
|
||||||
// just incase the inner scan method returned an error
|
// just incase the inner scan method returned an error
|
||||||
if (isPresent(output.error)) {
|
if (isPresent(output.error)) {
|
||||||
@ -209,7 +209,7 @@ export class CssScanner {
|
|||||||
next = new CssToken(this.index, this.column, this.line, CssTokenType.EOF, 'end of file');
|
next = new CssToken(this.index, this.column, this.line, CssTokenType.EOF, 'end of file');
|
||||||
}
|
}
|
||||||
|
|
||||||
var isMatchingType: boolean = false;
|
let isMatchingType: boolean = false;
|
||||||
if (type == CssTokenType.IdentifierOrNumber) {
|
if (type == CssTokenType.IdentifierOrNumber) {
|
||||||
// TODO (matsko): implement array traversal for lookup here
|
// TODO (matsko): implement array traversal for lookup here
|
||||||
isMatchingType = next.type == CssTokenType.Number || next.type == CssTokenType.Identifier;
|
isMatchingType = next.type == CssTokenType.Number || next.type == CssTokenType.Identifier;
|
||||||
@ -221,9 +221,9 @@ export class CssScanner {
|
|||||||
// mode so that the parser can recover...
|
// mode so that the parser can recover...
|
||||||
this.setMode(mode);
|
this.setMode(mode);
|
||||||
|
|
||||||
var error: CssScannerError = null;
|
let error: CssScannerError = null;
|
||||||
if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
|
if (!isMatchingType || (isPresent(value) && value != next.strValue)) {
|
||||||
var errorMessage =
|
let errorMessage =
|
||||||
CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value';
|
CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value';
|
||||||
|
|
||||||
if (isPresent(value)) {
|
if (isPresent(value)) {
|
||||||
@ -241,15 +241,15 @@ export class CssScanner {
|
|||||||
|
|
||||||
|
|
||||||
scan(): LexedCssResult {
|
scan(): LexedCssResult {
|
||||||
var trackWS = _trackWhitespace(this._currentMode);
|
const trackWS = _trackWhitespace(this._currentMode);
|
||||||
if (this.index == 0 && !trackWS) { // first scan
|
if (this.index == 0 && !trackWS) { // first scan
|
||||||
this.consumeWhitespace();
|
this.consumeWhitespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = this._scan();
|
const token = this._scan();
|
||||||
if (token == null) return null;
|
if (token == null) return null;
|
||||||
|
|
||||||
var error = this._currentError;
|
const error = this._currentError;
|
||||||
this._currentError = null;
|
this._currentError = null;
|
||||||
|
|
||||||
if (!trackWS) {
|
if (!trackWS) {
|
||||||
@ -260,14 +260,14 @@ export class CssScanner {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_scan(): CssToken {
|
_scan(): CssToken {
|
||||||
var peek = this.peek;
|
let peek = this.peek;
|
||||||
var peekPeek = this.peekPeek;
|
let peekPeek = this.peekPeek;
|
||||||
if (peek == chars.$EOF) return null;
|
if (peek == chars.$EOF) return null;
|
||||||
|
|
||||||
if (isCommentStart(peek, peekPeek)) {
|
if (isCommentStart(peek, peekPeek)) {
|
||||||
// even if comments are not tracked we still lex the
|
// even if comments are not tracked we still lex the
|
||||||
// comment so we can move the pointer forward
|
// comment so we can move the pointer forward
|
||||||
var commentToken = this.scanComment();
|
const commentToken = this.scanComment();
|
||||||
if (this._trackComments) {
|
if (this._trackComments) {
|
||||||
return commentToken;
|
return commentToken;
|
||||||
}
|
}
|
||||||
@ -290,9 +290,9 @@ export class CssScanner {
|
|||||||
return this.scanCssValueFunction();
|
return this.scanCssValueFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
var isModifier = peek == chars.$PLUS || peek == chars.$MINUS;
|
const isModifier = peek == chars.$PLUS || peek == chars.$MINUS;
|
||||||
var digitA = isModifier ? false : chars.isDigit(peek);
|
const digitA = isModifier ? false : chars.isDigit(peek);
|
||||||
var digitB = chars.isDigit(peekPeek);
|
const digitB = chars.isDigit(peekPeek);
|
||||||
if (digitA || (isModifier && (peekPeek == chars.$PERIOD || digitB)) ||
|
if (digitA || (isModifier && (peekPeek == chars.$PERIOD || digitB)) ||
|
||||||
(peek == chars.$PERIOD && digitB)) {
|
(peek == chars.$PERIOD && digitB)) {
|
||||||
return this.scanNumber();
|
return this.scanNumber();
|
||||||
@ -319,9 +319,9 @@ export class CssScanner {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
var startingLine = this.line;
|
const startingLine = this.line;
|
||||||
|
|
||||||
this.advance(); // /
|
this.advance(); // /
|
||||||
this.advance(); // *
|
this.advance(); // *
|
||||||
@ -336,18 +336,18 @@ export class CssScanner {
|
|||||||
this.advance(); // *
|
this.advance(); // *
|
||||||
this.advance(); // /
|
this.advance(); // /
|
||||||
|
|
||||||
var str = this.input.substring(start, this.index);
|
const str = this.input.substring(start, this.index);
|
||||||
return new CssToken(start, startingColumn, startingLine, CssTokenType.Comment, str);
|
return new CssToken(start, startingColumn, startingLine, CssTokenType.Comment, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanWhitespace(): CssToken {
|
scanWhitespace(): CssToken {
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
var startingLine = this.line;
|
const startingLine = this.line;
|
||||||
while (chars.isWhitespace(this.peek) && this.peek != chars.$EOF) {
|
while (chars.isWhitespace(this.peek) && this.peek != chars.$EOF) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var str = this.input.substring(start, this.index);
|
const str = this.input.substring(start, this.index);
|
||||||
return new CssToken(start, startingColumn, startingLine, CssTokenType.Whitespace, str);
|
return new CssToken(start, startingColumn, startingLine, CssTokenType.Whitespace, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,11 +357,11 @@ export class CssScanner {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var target = this.peek;
|
const target = this.peek;
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
var startingLine = this.line;
|
const startingLine = this.line;
|
||||||
var previous = target;
|
let previous = target;
|
||||||
this.advance();
|
this.advance();
|
||||||
|
|
||||||
while (!isCharMatch(target, previous, this.peek)) {
|
while (!isCharMatch(target, previous, this.peek)) {
|
||||||
@ -377,17 +377,17 @@ export class CssScanner {
|
|||||||
}
|
}
|
||||||
this.advance();
|
this.advance();
|
||||||
|
|
||||||
var str = this.input.substring(start, this.index);
|
const str = this.input.substring(start, this.index);
|
||||||
return new CssToken(start, startingColumn, startingLine, CssTokenType.String, str);
|
return new CssToken(start, startingColumn, startingLine, CssTokenType.String, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanNumber(): CssToken {
|
scanNumber(): CssToken {
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
if (this.peek == chars.$PLUS || this.peek == chars.$MINUS) {
|
if (this.peek == chars.$PLUS || this.peek == chars.$MINUS) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var periodUsed = false;
|
let periodUsed = false;
|
||||||
while (chars.isDigit(this.peek) || this.peek == chars.$PERIOD) {
|
while (chars.isDigit(this.peek) || this.peek == chars.$PERIOD) {
|
||||||
if (this.peek == chars.$PERIOD) {
|
if (this.peek == chars.$PERIOD) {
|
||||||
if (periodUsed) {
|
if (periodUsed) {
|
||||||
@ -397,7 +397,7 @@ export class CssScanner {
|
|||||||
}
|
}
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var strValue = this.input.substring(start, this.index);
|
const strValue = this.input.substring(start, this.index);
|
||||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Number, strValue);
|
return new CssToken(start, startingColumn, this.line, CssTokenType.Number, strValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,19 +407,19 @@ export class CssScanner {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
while (isIdentifierPart(this.peek)) {
|
while (isIdentifierPart(this.peek)) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var strValue = this.input.substring(start, this.index);
|
const strValue = this.input.substring(start, this.index);
|
||||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
|
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanCssValueFunction(): CssToken {
|
scanCssValueFunction(): CssToken {
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
var parenBalance = 1;
|
let parenBalance = 1;
|
||||||
while (this.peek != chars.$EOF && parenBalance > 0) {
|
while (this.peek != chars.$EOF && parenBalance > 0) {
|
||||||
this.advance();
|
this.advance();
|
||||||
if (this.peek == chars.$LPAREN) {
|
if (this.peek == chars.$LPAREN) {
|
||||||
@ -428,20 +428,20 @@ export class CssScanner {
|
|||||||
parenBalance--;
|
parenBalance--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var strValue = this.input.substring(start, this.index);
|
const strValue = this.input.substring(start, this.index);
|
||||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
|
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanCharacter(): CssToken {
|
scanCharacter(): CssToken {
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
if (this.assertCondition(
|
if (this.assertCondition(
|
||||||
isValidCssCharacter(this.peek, this._currentMode),
|
isValidCssCharacter(this.peek, this._currentMode),
|
||||||
charStr(this.peek) + ' is not a valid CSS character')) {
|
charStr(this.peek) + ' is not a valid CSS character')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var c = this.input.substring(start, start + 1);
|
const c = this.input.substring(start, start + 1);
|
||||||
this.advance();
|
this.advance();
|
||||||
|
|
||||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Character, c);
|
return new CssToken(start, startingColumn, this.line, CssTokenType.Character, c);
|
||||||
@ -452,12 +452,12 @@ export class CssScanner {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = this.index;
|
const start = this.index;
|
||||||
var startingColumn = this.column;
|
const startingColumn = this.column;
|
||||||
this.advance();
|
this.advance();
|
||||||
if (isIdentifierStart(this.peek, this.peekPeek)) {
|
if (isIdentifierStart(this.peek, this.peekPeek)) {
|
||||||
var ident = this.scanIdentifier();
|
const ident = this.scanIdentifier();
|
||||||
var strValue = '@' + ident.strValue;
|
const strValue = '@' + ident.strValue;
|
||||||
return new CssToken(start, startingColumn, this.line, CssTokenType.AtKeyword, strValue);
|
return new CssToken(start, startingColumn, this.line, CssTokenType.AtKeyword, strValue);
|
||||||
} else {
|
} else {
|
||||||
return this.scanCharacter();
|
return this.scanCharacter();
|
||||||
@ -473,12 +473,12 @@ export class CssScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
error(message: string, errorTokenValue: string = null, doNotAdvance: boolean = false): CssToken {
|
error(message: string, errorTokenValue: string = null, doNotAdvance: boolean = false): CssToken {
|
||||||
var index: number = this.index;
|
const index: number = this.index;
|
||||||
var column: number = this.column;
|
const column: number = this.column;
|
||||||
var line: number = this.line;
|
const line: number = this.line;
|
||||||
errorTokenValue = errorTokenValue || String.fromCharCode(this.peek);
|
errorTokenValue = errorTokenValue || String.fromCharCode(this.peek);
|
||||||
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
|
const invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
|
||||||
var errorMessage =
|
const errorMessage =
|
||||||
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
|
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
|
||||||
if (!doNotAdvance) {
|
if (!doNotAdvance) {
|
||||||
this.advance();
|
this.advance();
|
||||||
@ -501,7 +501,7 @@ function isCommentEnd(code: number, next: number): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isStringStart(code: number, next: number): boolean {
|
function isStringStart(code: number, next: number): boolean {
|
||||||
var target = code;
|
let target = code;
|
||||||
if (target == chars.$BACKSLASH) {
|
if (target == chars.$BACKSLASH) {
|
||||||
target = next;
|
target = next;
|
||||||
}
|
}
|
||||||
@ -509,7 +509,7 @@ function isStringStart(code: number, next: number): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isIdentifierStart(code: number, next: number): boolean {
|
function isIdentifierStart(code: number, next: number): boolean {
|
||||||
var target = code;
|
let target = code;
|
||||||
if (target == chars.$MINUS) {
|
if (target == chars.$MINUS) {
|
||||||
target = next;
|
target = next;
|
||||||
}
|
}
|
||||||
|
@ -93,16 +93,16 @@ export class CssParser {
|
|||||||
* @param url the name of the CSS file containing the CSS source code
|
* @param url the name of the CSS file containing the CSS source code
|
||||||
*/
|
*/
|
||||||
parse(css: string, url: string): ParsedCssResult {
|
parse(css: string, url: string): ParsedCssResult {
|
||||||
var lexer = new CssLexer();
|
const lexer = new CssLexer();
|
||||||
this._file = new ParseSourceFile(css, url);
|
this._file = new ParseSourceFile(css, url);
|
||||||
this._scanner = lexer.scan(css, false);
|
this._scanner = lexer.scan(css, false);
|
||||||
|
|
||||||
var ast = this._parseStyleSheet(EOF_DELIM_FLAG);
|
const ast = this._parseStyleSheet(EOF_DELIM_FLAG);
|
||||||
|
|
||||||
var errors = this._errors;
|
const errors = this._errors;
|
||||||
this._errors = [];
|
this._errors = [];
|
||||||
|
|
||||||
var result = new ParsedCssResult(errors, ast);
|
const result = new ParsedCssResult(errors, ast);
|
||||||
this._file = null;
|
this._file = null;
|
||||||
this._scanner = null;
|
this._scanner = null;
|
||||||
return result;
|
return result;
|
||||||
@ -110,15 +110,15 @@ export class CssParser {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_parseStyleSheet(delimiters: number): CssStyleSheetAst {
|
_parseStyleSheet(delimiters: number): CssStyleSheetAst {
|
||||||
var results: CssRuleAst[] = [];
|
const results: CssRuleAst[] = [];
|
||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
while (this._scanner.peek != chars.$EOF) {
|
while (this._scanner.peek != chars.$EOF) {
|
||||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||||
results.push(this._parseRule(delimiters));
|
results.push(this._parseRule(delimiters));
|
||||||
}
|
}
|
||||||
var span: ParseSourceSpan = null;
|
let span: ParseSourceSpan = null;
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
var firstRule = results[0];
|
const firstRule = results[0];
|
||||||
// we collect the last token like so incase there was an
|
// we collect the last token like so incase there was an
|
||||||
// EOF token that was emitted sometime during the lexing
|
// EOF token that was emitted sometime during the lexing
|
||||||
span = this._generateSourceSpan(firstRule, this._lastToken);
|
span = this._generateSourceSpan(firstRule, this._lastToken);
|
||||||
@ -136,11 +136,11 @@ export class CssParser {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_generateSourceSpan(start: CssToken|CssAst, end: CssToken|CssAst = null): ParseSourceSpan {
|
_generateSourceSpan(start: CssToken|CssAst, end: CssToken|CssAst = null): ParseSourceSpan {
|
||||||
var startLoc: ParseLocation;
|
let startLoc: ParseLocation;
|
||||||
if (start instanceof CssAst) {
|
if (start instanceof CssAst) {
|
||||||
startLoc = start.location.start;
|
startLoc = start.location.start;
|
||||||
} else {
|
} else {
|
||||||
var token = start;
|
let token = start;
|
||||||
if (!isPresent(token)) {
|
if (!isPresent(token)) {
|
||||||
// the data here is invalid, however, if and when this does
|
// the data here is invalid, however, if and when this does
|
||||||
// occur, any other errors associated with this will be collected
|
// occur, any other errors associated with this will be collected
|
||||||
@ -153,9 +153,9 @@ export class CssParser {
|
|||||||
end = this._lastToken;
|
end = this._lastToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
var endLine: number;
|
let endLine: number;
|
||||||
var endColumn: number;
|
let endColumn: number;
|
||||||
var endIndex: number;
|
let endIndex: number;
|
||||||
if (end instanceof CssAst) {
|
if (end instanceof CssAst) {
|
||||||
endLine = end.location.end.line;
|
endLine = end.location.end.line;
|
||||||
endColumn = end.location.end.col;
|
endColumn = end.location.end.col;
|
||||||
@ -166,7 +166,7 @@ export class CssParser {
|
|||||||
endIndex = end.index;
|
endIndex = end.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
var endLoc = new ParseLocation(this._file, endIndex, endLine, endColumn);
|
const endLoc = new ParseLocation(this._file, endIndex, endLine, endColumn);
|
||||||
return new ParseSourceSpan(startLoc, endLoc);
|
return new ParseSourceSpan(startLoc, endLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,21 +224,21 @@ export class CssParser {
|
|||||||
const start = this._getScannerIndex();
|
const start = this._getScannerIndex();
|
||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||||
var token = this._scan();
|
const token = this._scan();
|
||||||
var startToken = token;
|
const startToken = token;
|
||||||
|
|
||||||
this._assertCondition(
|
this._assertCondition(
|
||||||
token.type == CssTokenType.AtKeyword,
|
token.type == CssTokenType.AtKeyword,
|
||||||
`The CSS Rule ${token.strValue} is not a valid [@] rule.`, token);
|
`The CSS Rule ${token.strValue} is not a valid [@] rule.`, token);
|
||||||
|
|
||||||
var block: CssBlockAst;
|
let block: CssBlockAst;
|
||||||
var type = this._resolveBlockType(token);
|
const type = this._resolveBlockType(token);
|
||||||
var span: ParseSourceSpan;
|
let span: ParseSourceSpan;
|
||||||
var tokens: CssToken[];
|
let tokens: CssToken[];
|
||||||
var endToken: CssToken;
|
let endToken: CssToken;
|
||||||
var end: number;
|
let end: number;
|
||||||
var strValue: string;
|
let strValue: string;
|
||||||
var query: CssAtRulePredicateAst;
|
let query: CssAtRulePredicateAst;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BlockType.Charset:
|
case BlockType.Charset:
|
||||||
case BlockType.Namespace:
|
case BlockType.Namespace:
|
||||||
@ -324,23 +324,23 @@ export class CssParser {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_parseSelectorRule(delimiters: number): CssRuleAst {
|
_parseSelectorRule(delimiters: number): CssRuleAst {
|
||||||
const start = this._getScannerIndex();
|
const start = this._getScannerIndex();
|
||||||
var selectors = this._parseSelectors(delimiters);
|
const selectors = this._parseSelectors(delimiters);
|
||||||
var block = this._parseStyleBlock(delimiters);
|
const block = this._parseStyleBlock(delimiters);
|
||||||
var ruleAst: CssRuleAst;
|
let ruleAst: CssRuleAst;
|
||||||
var span: ParseSourceSpan;
|
let span: ParseSourceSpan;
|
||||||
var startSelector = selectors[0];
|
const startSelector = selectors[0];
|
||||||
if (isPresent(block)) {
|
if (isPresent(block)) {
|
||||||
span = this._generateSourceSpan(startSelector, block);
|
span = this._generateSourceSpan(startSelector, block);
|
||||||
ruleAst = new CssSelectorRuleAst(span, selectors, block);
|
ruleAst = new CssSelectorRuleAst(span, selectors, block);
|
||||||
} else {
|
} else {
|
||||||
var name = this._extractSourceContent(start, this._getScannerIndex() - 1);
|
const name = this._extractSourceContent(start, this._getScannerIndex() - 1);
|
||||||
var innerTokens: CssToken[] = [];
|
const innerTokens: CssToken[] = [];
|
||||||
selectors.forEach((selector: CssSelectorAst) => {
|
selectors.forEach((selector: CssSelectorAst) => {
|
||||||
selector.selectorParts.forEach((part: CssSimpleSelectorAst) => {
|
selector.selectorParts.forEach((part: CssSimpleSelectorAst) => {
|
||||||
part.tokens.forEach((token: CssToken) => { innerTokens.push(token); });
|
part.tokens.forEach((token: CssToken) => { innerTokens.push(token); });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var endToken = innerTokens[innerTokens.length - 1];
|
const endToken = innerTokens[innerTokens.length - 1];
|
||||||
span = this._generateSourceSpan(startSelector, endToken);
|
span = this._generateSourceSpan(startSelector, endToken);
|
||||||
ruleAst = new CssUnknownTokenListAst(span, name, innerTokens);
|
ruleAst = new CssUnknownTokenListAst(span, name, innerTokens);
|
||||||
}
|
}
|
||||||
@ -353,8 +353,8 @@ export class CssParser {
|
|||||||
_parseSelectors(delimiters: number): CssSelectorAst[] {
|
_parseSelectors(delimiters: number): CssSelectorAst[] {
|
||||||
delimiters |= LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG;
|
delimiters |= LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG;
|
||||||
|
|
||||||
var selectors: CssSelectorAst[] = [];
|
const selectors: CssSelectorAst[] = [];
|
||||||
var isParsingSelectors = true;
|
let isParsingSelectors = true;
|
||||||
while (isParsingSelectors) {
|
while (isParsingSelectors) {
|
||||||
selectors.push(this._parseSelector(delimiters));
|
selectors.push(this._parseSelector(delimiters));
|
||||||
|
|
||||||
@ -374,9 +374,9 @@ export class CssParser {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_scan(): CssToken {
|
_scan(): CssToken {
|
||||||
var output = this._scanner.scan();
|
const output = this._scanner.scan();
|
||||||
var token = output.token;
|
const token = output.token;
|
||||||
var error = output.error;
|
const error = output.error;
|
||||||
if (isPresent(error)) {
|
if (isPresent(error)) {
|
||||||
this._error(error.rawMessage, token);
|
this._error(error.rawMessage, token);
|
||||||
}
|
}
|
||||||
@ -389,9 +389,9 @@ export class CssParser {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_consume(type: CssTokenType, value: string = null): CssToken {
|
_consume(type: CssTokenType, value: string = null): CssToken {
|
||||||
var output = this._scanner.consume(type, value);
|
const output = this._scanner.consume(type, value);
|
||||||
var token = output.token;
|
const token = output.token;
|
||||||
var error = output.error;
|
const error = output.error;
|
||||||
if (isPresent(error)) {
|
if (isPresent(error)) {
|
||||||
this._error(error.rawMessage, token);
|
this._error(error.rawMessage, token);
|
||||||
}
|
}
|
||||||
@ -404,23 +404,23 @@ export class CssParser {
|
|||||||
delimiters |= RBRACE_DELIM_FLAG;
|
delimiters |= RBRACE_DELIM_FLAG;
|
||||||
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
|
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
|
||||||
|
|
||||||
var startToken = this._consume(CssTokenType.Character, '{');
|
const startToken = this._consume(CssTokenType.Character, '{');
|
||||||
|
|
||||||
var definitions: CssKeyframeDefinitionAst[] = [];
|
const definitions: CssKeyframeDefinitionAst[] = [];
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
definitions.push(this._parseKeyframeDefinition(delimiters));
|
definitions.push(this._parseKeyframeDefinition(delimiters));
|
||||||
}
|
}
|
||||||
|
|
||||||
var endToken = this._consume(CssTokenType.Character, '}');
|
const endToken = this._consume(CssTokenType.Character, '}');
|
||||||
|
|
||||||
var span = this._generateSourceSpan(startToken, endToken);
|
const span = this._generateSourceSpan(startToken, endToken);
|
||||||
return new CssBlockAst(span, definitions);
|
return new CssBlockAst(span, definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_parseKeyframeDefinition(delimiters: number): CssKeyframeDefinitionAst {
|
_parseKeyframeDefinition(delimiters: number): CssKeyframeDefinitionAst {
|
||||||
const start = this._getScannerIndex();
|
const start = this._getScannerIndex();
|
||||||
var stepTokens: CssToken[] = [];
|
const stepTokens: CssToken[] = [];
|
||||||
delimiters |= LBRACE_DELIM_FLAG;
|
delimiters |= LBRACE_DELIM_FLAG;
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
stepTokens.push(this._parseKeyframeLabel(delimiters | COMMA_DELIM_FLAG));
|
stepTokens.push(this._parseKeyframeLabel(delimiters | COMMA_DELIM_FLAG));
|
||||||
@ -428,9 +428,9 @@ export class CssParser {
|
|||||||
this._consume(CssTokenType.Character, ',');
|
this._consume(CssTokenType.Character, ',');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stylesBlock = this._parseStyleBlock(delimiters | RBRACE_DELIM_FLAG);
|
const stylesBlock = this._parseStyleBlock(delimiters | RBRACE_DELIM_FLAG);
|
||||||
var span = this._generateSourceSpan(stepTokens[0], stylesBlock);
|
const span = this._generateSourceSpan(stepTokens[0], stylesBlock);
|
||||||
var ast = new CssKeyframeDefinitionAst(span, stepTokens, stylesBlock);
|
const ast = new CssKeyframeDefinitionAst(span, stepTokens, stylesBlock);
|
||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||||
return ast;
|
return ast;
|
||||||
@ -449,34 +449,34 @@ export class CssParser {
|
|||||||
delimiters &= ~COMMA_DELIM_FLAG;
|
delimiters &= ~COMMA_DELIM_FLAG;
|
||||||
|
|
||||||
// we keep the original value since we may use it to recurse when :not, :host are used
|
// we keep the original value since we may use it to recurse when :not, :host are used
|
||||||
var startingDelims = delimiters;
|
const startingDelims = delimiters;
|
||||||
|
|
||||||
var startToken = this._consume(CssTokenType.Character, ':');
|
const startToken = this._consume(CssTokenType.Character, ':');
|
||||||
var tokens = [startToken];
|
const tokens = [startToken];
|
||||||
|
|
||||||
if (this._scanner.peek == chars.$COLON) { // ::something
|
if (this._scanner.peek == chars.$COLON) { // ::something
|
||||||
tokens.push(this._consume(CssTokenType.Character, ':'));
|
tokens.push(this._consume(CssTokenType.Character, ':'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerSelectors: CssSelectorAst[] = [];
|
const innerSelectors: CssSelectorAst[] = [];
|
||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR);
|
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR);
|
||||||
|
|
||||||
// host, host-context, lang, not, nth-child are all identifiers
|
// host, host-context, lang, not, nth-child are all identifiers
|
||||||
var pseudoSelectorToken = this._consume(CssTokenType.Identifier);
|
const pseudoSelectorToken = this._consume(CssTokenType.Identifier);
|
||||||
var pseudoSelectorName = pseudoSelectorToken.strValue;
|
const pseudoSelectorName = pseudoSelectorToken.strValue;
|
||||||
tokens.push(pseudoSelectorToken);
|
tokens.push(pseudoSelectorToken);
|
||||||
|
|
||||||
// host(), lang(), nth-child(), etc...
|
// host(), lang(), nth-child(), etc...
|
||||||
if (this._scanner.peek == chars.$LPAREN) {
|
if (this._scanner.peek == chars.$LPAREN) {
|
||||||
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS);
|
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS);
|
||||||
|
|
||||||
var openParenToken = this._consume(CssTokenType.Character, '(');
|
const openParenToken = this._consume(CssTokenType.Character, '(');
|
||||||
tokens.push(openParenToken);
|
tokens.push(openParenToken);
|
||||||
|
|
||||||
// :host(innerSelector(s)), :not(selector), etc...
|
// :host(innerSelector(s)), :not(selector), etc...
|
||||||
if (_pseudoSelectorSupportsInnerSelectors(pseudoSelectorName)) {
|
if (_pseudoSelectorSupportsInnerSelectors(pseudoSelectorName)) {
|
||||||
var innerDelims = startingDelims | LPAREN_DELIM_FLAG | RPAREN_DELIM_FLAG;
|
let innerDelims = startingDelims | LPAREN_DELIM_FLAG | RPAREN_DELIM_FLAG;
|
||||||
if (pseudoSelectorName == 'not') {
|
if (pseudoSelectorName == 'not') {
|
||||||
// the inner selector inside of :not(...) can only be one
|
// the inner selector inside of :not(...) can only be one
|
||||||
// CSS selector (no commas allowed) ... This is according
|
// CSS selector (no commas allowed) ... This is according
|
||||||
@ -491,23 +491,23 @@ export class CssParser {
|
|||||||
} else {
|
} else {
|
||||||
// this branch is for things like "en-us, 2k + 1, etc..."
|
// this branch is for things like "en-us, 2k + 1, etc..."
|
||||||
// which all end up in pseudoSelectors like :lang, :nth-child, etc..
|
// which all end up in pseudoSelectors like :lang, :nth-child, etc..
|
||||||
var innerValueDelims = delimiters | LBRACE_DELIM_FLAG | COLON_DELIM_FLAG |
|
const innerValueDelims = delimiters | LBRACE_DELIM_FLAG | COLON_DELIM_FLAG |
|
||||||
RPAREN_DELIM_FLAG | LPAREN_DELIM_FLAG;
|
RPAREN_DELIM_FLAG | LPAREN_DELIM_FLAG;
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, innerValueDelims)) {
|
while (!characterContainsDelimiter(this._scanner.peek, innerValueDelims)) {
|
||||||
var token = this._scan();
|
const token = this._scan();
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var closeParenToken = this._consume(CssTokenType.Character, ')');
|
const closeParenToken = this._consume(CssTokenType.Character, ')');
|
||||||
tokens.push(closeParenToken);
|
tokens.push(closeParenToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
const end = this._getScannerIndex() - 1;
|
const end = this._getScannerIndex() - 1;
|
||||||
var strValue = this._extractSourceContent(start, end);
|
const strValue = this._extractSourceContent(start, end);
|
||||||
|
|
||||||
var endToken = tokens[tokens.length - 1];
|
const endToken = tokens[tokens.length - 1];
|
||||||
var span = this._generateSourceSpan(startToken, endToken);
|
const span = this._generateSourceSpan(startToken, endToken);
|
||||||
return new CssPseudoSelectorAst(span, strValue, pseudoSelectorName, tokens, innerSelectors);
|
return new CssPseudoSelectorAst(span, strValue, pseudoSelectorName, tokens, innerSelectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,21 +518,21 @@ export class CssParser {
|
|||||||
delimiters |= COMMA_DELIM_FLAG;
|
delimiters |= COMMA_DELIM_FLAG;
|
||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.SELECTOR);
|
this._scanner.setMode(CssLexerMode.SELECTOR);
|
||||||
var selectorCssTokens: CssToken[] = [];
|
const selectorCssTokens: CssToken[] = [];
|
||||||
var pseudoSelectors: CssPseudoSelectorAst[] = [];
|
const pseudoSelectors: CssPseudoSelectorAst[] = [];
|
||||||
|
|
||||||
var previousToken: CssToken;
|
let previousToken: CssToken;
|
||||||
|
|
||||||
var selectorPartDelimiters = delimiters | SPACE_DELIM_FLAG;
|
const selectorPartDelimiters = delimiters | SPACE_DELIM_FLAG;
|
||||||
var loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters);
|
let loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters);
|
||||||
|
|
||||||
var hasAttributeError = false;
|
let hasAttributeError = false;
|
||||||
while (loopOverSelector) {
|
while (loopOverSelector) {
|
||||||
var peek = this._scanner.peek;
|
const peek = this._scanner.peek;
|
||||||
|
|
||||||
switch (peek) {
|
switch (peek) {
|
||||||
case chars.$COLON:
|
case chars.$COLON:
|
||||||
var innerPseudo = this._parsePseudoSelector(delimiters);
|
let innerPseudo = this._parsePseudoSelector(delimiters);
|
||||||
pseudoSelectors.push(innerPseudo);
|
pseudoSelectors.push(innerPseudo);
|
||||||
this._scanner.setMode(CssLexerMode.SELECTOR);
|
this._scanner.setMode(CssLexerMode.SELECTOR);
|
||||||
break;
|
break;
|
||||||
@ -561,7 +561,7 @@ export class CssParser {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = this._scan();
|
let token = this._scan();
|
||||||
previousToken = token;
|
previousToken = token;
|
||||||
selectorCssTokens.push(token);
|
selectorCssTokens.push(token);
|
||||||
break;
|
break;
|
||||||
@ -578,18 +578,18 @@ export class CssParser {
|
|||||||
previousToken);
|
previousToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
var end = this._getScannerIndex() - 1;
|
let end = this._getScannerIndex() - 1;
|
||||||
|
|
||||||
// this happens if the selector is not directly followed by
|
// this happens if the selector is not directly followed by
|
||||||
// a comma or curly brace without a space in between
|
// a comma or curly brace without a space in between
|
||||||
|
let operator: CssToken = null;
|
||||||
|
let operatorScanCount = 0;
|
||||||
|
let lastOperatorToken: CssToken = null;
|
||||||
if (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
if (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
var operator: CssToken = null;
|
|
||||||
var operatorScanCount = 0;
|
|
||||||
var lastOperatorToken: CssToken = null;
|
|
||||||
while (operator == null && !characterContainsDelimiter(this._scanner.peek, delimiters) &&
|
while (operator == null && !characterContainsDelimiter(this._scanner.peek, delimiters) &&
|
||||||
isSelectorOperatorCharacter(this._scanner.peek)) {
|
isSelectorOperatorCharacter(this._scanner.peek)) {
|
||||||
var token = this._scan();
|
let token = this._scan();
|
||||||
var tokenOperator = token.strValue;
|
const tokenOperator = token.strValue;
|
||||||
operatorScanCount++;
|
operatorScanCount++;
|
||||||
lastOperatorToken = token;
|
lastOperatorToken = token;
|
||||||
if (tokenOperator != SPACE_OPERATOR) {
|
if (tokenOperator != SPACE_OPERATOR) {
|
||||||
@ -607,7 +607,7 @@ export class CssParser {
|
|||||||
lastOperatorToken.index, lastOperatorToken.column, lastOperatorToken.line,
|
lastOperatorToken.index, lastOperatorToken.column, lastOperatorToken.line,
|
||||||
CssTokenType.Identifier, DEEP_OPERATOR_STR);
|
CssTokenType.Identifier, DEEP_OPERATOR_STR);
|
||||||
} else {
|
} else {
|
||||||
let text = SLASH_CHARACTER + deepToken.strValue + deepSlash.strValue;
|
const text = SLASH_CHARACTER + deepToken.strValue + deepSlash.strValue;
|
||||||
this._error(
|
this._error(
|
||||||
generateErrorMessage(
|
generateErrorMessage(
|
||||||
this._getSourceContent(), `${text} is an invalid CSS operator`, text, index,
|
this._getSourceContent(), `${text} is an invalid CSS operator`, text, index,
|
||||||
@ -643,7 +643,7 @@ export class CssParser {
|
|||||||
|
|
||||||
this._scanner.consumeWhitespace();
|
this._scanner.consumeWhitespace();
|
||||||
|
|
||||||
var strValue = this._extractSourceContent(start, end);
|
const strValue = this._extractSourceContent(start, end);
|
||||||
|
|
||||||
// if we do come across one or more spaces inside of
|
// if we do come across one or more spaces inside of
|
||||||
// the operators loop then an empty space is still a
|
// the operators loop then an empty space is still a
|
||||||
@ -654,8 +654,8 @@ export class CssParser {
|
|||||||
|
|
||||||
// please note that `endToken` is reassigned multiple times below
|
// please note that `endToken` is reassigned multiple times below
|
||||||
// so please do not optimize the if statements into if/elseif
|
// so please do not optimize the if statements into if/elseif
|
||||||
var startTokenOrAst: CssToken|CssAst = null;
|
let startTokenOrAst: CssToken|CssAst = null;
|
||||||
var endTokenOrAst: CssToken|CssAst = null;
|
let endTokenOrAst: CssToken|CssAst = null;
|
||||||
if (selectorCssTokens.length > 0) {
|
if (selectorCssTokens.length > 0) {
|
||||||
startTokenOrAst = startTokenOrAst || selectorCssTokens[0];
|
startTokenOrAst = startTokenOrAst || selectorCssTokens[0];
|
||||||
endTokenOrAst = selectorCssTokens[selectorCssTokens.length - 1];
|
endTokenOrAst = selectorCssTokens[selectorCssTokens.length - 1];
|
||||||
@ -669,7 +669,7 @@ export class CssParser {
|
|||||||
endTokenOrAst = operator;
|
endTokenOrAst = operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
var span = this._generateSourceSpan(startTokenOrAst, endTokenOrAst);
|
const span = this._generateSourceSpan(startTokenOrAst, endTokenOrAst);
|
||||||
return new CssSimpleSelectorAst(span, selectorCssTokens, strValue, pseudoSelectors, operator);
|
return new CssSimpleSelectorAst(span, selectorCssTokens, strValue, pseudoSelectors, operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,15 +678,15 @@ export class CssParser {
|
|||||||
delimiters |= COMMA_DELIM_FLAG;
|
delimiters |= COMMA_DELIM_FLAG;
|
||||||
this._scanner.setMode(CssLexerMode.SELECTOR);
|
this._scanner.setMode(CssLexerMode.SELECTOR);
|
||||||
|
|
||||||
var simpleSelectors: CssSimpleSelectorAst[] = [];
|
const simpleSelectors: CssSimpleSelectorAst[] = [];
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
simpleSelectors.push(this._parseSimpleSelector(delimiters));
|
simpleSelectors.push(this._parseSimpleSelector(delimiters));
|
||||||
this._scanner.consumeWhitespace();
|
this._scanner.consumeWhitespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstSelector = simpleSelectors[0];
|
const firstSelector = simpleSelectors[0];
|
||||||
var lastSelector = simpleSelectors[simpleSelectors.length - 1];
|
const lastSelector = simpleSelectors[simpleSelectors.length - 1];
|
||||||
var span = this._generateSourceSpan(firstSelector, lastSelector);
|
const span = this._generateSourceSpan(firstSelector, lastSelector);
|
||||||
return new CssSelectorAst(span, simpleSelectors);
|
return new CssSelectorAst(span, simpleSelectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,11 +697,11 @@ export class CssParser {
|
|||||||
this._scanner.setMode(CssLexerMode.STYLE_VALUE);
|
this._scanner.setMode(CssLexerMode.STYLE_VALUE);
|
||||||
const start = this._getScannerIndex();
|
const start = this._getScannerIndex();
|
||||||
|
|
||||||
var tokens: CssToken[] = [];
|
const tokens: CssToken[] = [];
|
||||||
var wsStr = '';
|
let wsStr = '';
|
||||||
var previous: CssToken;
|
let previous: CssToken;
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
var token: CssToken;
|
let token: CssToken;
|
||||||
if (isPresent(previous) && previous.type == CssTokenType.Identifier &&
|
if (isPresent(previous) && previous.type == CssTokenType.Identifier &&
|
||||||
this._scanner.peek == chars.$LPAREN) {
|
this._scanner.peek == chars.$LPAREN) {
|
||||||
token = this._consume(CssTokenType.Character, '(');
|
token = this._consume(CssTokenType.Character, '(');
|
||||||
@ -731,7 +731,7 @@ export class CssParser {
|
|||||||
const end = this._getScannerIndex() - 1;
|
const end = this._getScannerIndex() - 1;
|
||||||
this._scanner.consumeWhitespace();
|
this._scanner.consumeWhitespace();
|
||||||
|
|
||||||
var code = this._scanner.peek;
|
const code = this._scanner.peek;
|
||||||
if (code == chars.$SEMICOLON) {
|
if (code == chars.$SEMICOLON) {
|
||||||
this._consume(CssTokenType.Character, ';');
|
this._consume(CssTokenType.Character, ';');
|
||||||
} else if (code != chars.$RBRACE) {
|
} else if (code != chars.$RBRACE) {
|
||||||
@ -742,18 +742,18 @@ export class CssParser {
|
|||||||
previous);
|
previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
var strValue = this._extractSourceContent(start, end);
|
const strValue = this._extractSourceContent(start, end);
|
||||||
var startToken = tokens[0];
|
const startToken = tokens[0];
|
||||||
var endToken = tokens[tokens.length - 1];
|
const endToken = tokens[tokens.length - 1];
|
||||||
var span = this._generateSourceSpan(startToken, endToken);
|
const span = this._generateSourceSpan(startToken, endToken);
|
||||||
return new CssStyleValueAst(span, tokens, strValue);
|
return new CssStyleValueAst(span, tokens, strValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_collectUntilDelim(delimiters: number, assertType: CssTokenType = null): CssToken[] {
|
_collectUntilDelim(delimiters: number, assertType: CssTokenType = null): CssToken[] {
|
||||||
var tokens: CssToken[] = [];
|
const tokens: CssToken[] = [];
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
var val = isPresent(assertType) ? this._consume(assertType) : this._scan();
|
const val = isPresent(assertType) ? this._consume(assertType) : this._scan();
|
||||||
tokens.push(val);
|
tokens.push(val);
|
||||||
}
|
}
|
||||||
return tokens;
|
return tokens;
|
||||||
@ -765,20 +765,20 @@ export class CssParser {
|
|||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||||
|
|
||||||
var startToken = this._consume(CssTokenType.Character, '{');
|
const startToken = this._consume(CssTokenType.Character, '{');
|
||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
|
|
||||||
var results: CssRuleAst[] = [];
|
const results: CssRuleAst[] = [];
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
results.push(this._parseRule(delimiters));
|
results.push(this._parseRule(delimiters));
|
||||||
}
|
}
|
||||||
|
|
||||||
var endToken = this._consume(CssTokenType.Character, '}');
|
const endToken = this._consume(CssTokenType.Character, '}');
|
||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
|
|
||||||
var span = this._generateSourceSpan(startToken, endToken);
|
const span = this._generateSourceSpan(startToken, endToken);
|
||||||
return new CssBlockAst(span, results);
|
return new CssBlockAst(span, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,12 +788,12 @@ export class CssParser {
|
|||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
||||||
|
|
||||||
var startToken = this._consume(CssTokenType.Character, '{');
|
const startToken = this._consume(CssTokenType.Character, '{');
|
||||||
if (startToken.numValue != chars.$LBRACE) {
|
if (startToken.numValue != chars.$LBRACE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var definitions: CssDefinitionAst[] = [];
|
const definitions: CssDefinitionAst[] = [];
|
||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
|
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
@ -801,12 +801,12 @@ export class CssParser {
|
|||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
}
|
}
|
||||||
|
|
||||||
var endToken = this._consume(CssTokenType.Character, '}');
|
const endToken = this._consume(CssTokenType.Character, '}');
|
||||||
|
|
||||||
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
|
|
||||||
var span = this._generateSourceSpan(startToken, endToken);
|
const span = this._generateSourceSpan(startToken, endToken);
|
||||||
return new CssStylesBlockAst(span, definitions);
|
return new CssStylesBlockAst(span, definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,10 +814,10 @@ export class CssParser {
|
|||||||
_parseDefinition(delimiters: number): CssDefinitionAst {
|
_parseDefinition(delimiters: number): CssDefinitionAst {
|
||||||
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
||||||
|
|
||||||
var prop = this._consume(CssTokenType.Identifier);
|
let prop = this._consume(CssTokenType.Identifier);
|
||||||
var parseValue: boolean = false;
|
let parseValue: boolean = false;
|
||||||
var value: CssStyleValueAst = null;
|
let value: CssStyleValueAst = null;
|
||||||
var endToken: CssToken|CssStyleValueAst = prop;
|
let endToken: CssToken|CssStyleValueAst = prop;
|
||||||
|
|
||||||
// the colon value separates the prop from the style.
|
// the colon value separates the prop from the style.
|
||||||
// there are a few cases as to what could happen if it
|
// there are a few cases as to what could happen if it
|
||||||
@ -830,13 +830,13 @@ export class CssParser {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var propStr = [prop.strValue];
|
let propStr = [prop.strValue];
|
||||||
if (this._scanner.peek != chars.$COLON) {
|
if (this._scanner.peek != chars.$COLON) {
|
||||||
// this will throw the error
|
// this will throw the error
|
||||||
var nextValue = this._consume(CssTokenType.Character, ':');
|
const nextValue = this._consume(CssTokenType.Character, ':');
|
||||||
propStr.push(nextValue.strValue);
|
propStr.push(nextValue.strValue);
|
||||||
|
|
||||||
var remainingTokens = this._collectUntilDelim(
|
const remainingTokens = this._collectUntilDelim(
|
||||||
delimiters | COLON_DELIM_FLAG | SEMICOLON_DELIM_FLAG, CssTokenType.Identifier);
|
delimiters | COLON_DELIM_FLAG | SEMICOLON_DELIM_FLAG, CssTokenType.Identifier);
|
||||||
if (remainingTokens.length > 0) {
|
if (remainingTokens.length > 0) {
|
||||||
remainingTokens.forEach((token) => { propStr.push(token.strValue); });
|
remainingTokens.forEach((token) => { propStr.push(token.strValue); });
|
||||||
@ -865,7 +865,7 @@ export class CssParser {
|
|||||||
prop);
|
prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
var span = this._generateSourceSpan(prop, endToken);
|
const span = this._generateSourceSpan(prop, endToken);
|
||||||
return new CssDefinitionAst(span, prop, value);
|
return new CssDefinitionAst(span, prop, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,8 +880,8 @@ export class CssParser {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_error(message: string, problemToken: CssToken) {
|
_error(message: string, problemToken: CssToken) {
|
||||||
var length = problemToken.strValue.length;
|
const length = problemToken.strValue.length;
|
||||||
var error = CssParseError.create(
|
const error = CssParseError.create(
|
||||||
this._file, 0, problemToken.line, problemToken.column, length, message);
|
this._file, 0, problemToken.line, problemToken.column, length, message);
|
||||||
this._errors.push(error);
|
this._errors.push(error);
|
||||||
}
|
}
|
||||||
@ -891,9 +891,9 @@ export class CssParseError extends ParseError {
|
|||||||
static create(
|
static create(
|
||||||
file: ParseSourceFile, offset: number, line: number, col: number, length: number,
|
file: ParseSourceFile, offset: number, line: number, col: number, length: number,
|
||||||
errMsg: string): CssParseError {
|
errMsg: string): CssParseError {
|
||||||
var start = new ParseLocation(file, offset, line, col);
|
const start = new ParseLocation(file, offset, line, col);
|
||||||
var end = new ParseLocation(file, offset, line, col + length);
|
const end = new ParseLocation(file, offset, line, col + length);
|
||||||
var span = new ParseSourceSpan(start, end);
|
const span = new ParseSourceSpan(start, end);
|
||||||
return new CssParseError(span, 'CSS Parse Error: ' + errMsg);
|
return new CssParseError(span, 'CSS Parse Error: ' + errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,11 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, ViewEncapsulation} from '@angular/core';
|
import {Component, Injectable, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {MapWrapper} from './facade/collection';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
|
||||||
import * as html from './ml_parser/ast';
|
import * as html from './ml_parser/ast';
|
||||||
import {HtmlParser} from './ml_parser/html_parser';
|
import {HtmlParser} from './ml_parser/html_parser';
|
||||||
import {InterpolationConfig} from './ml_parser/interpolation_config';
|
import {InterpolationConfig} from './ml_parser/interpolation_config';
|
||||||
@ -21,6 +20,18 @@ import {PreparsedElementType, preparseElement} from './template_parser/template_
|
|||||||
import {UrlResolver} from './url_resolver';
|
import {UrlResolver} from './url_resolver';
|
||||||
import {SyncAsyncResult} from './util';
|
import {SyncAsyncResult} from './util';
|
||||||
|
|
||||||
|
export interface PrenormalizedTemplateMetadata {
|
||||||
|
componentType: any;
|
||||||
|
moduleUrl: string;
|
||||||
|
template?: string;
|
||||||
|
templateUrl?: string;
|
||||||
|
styles?: string[];
|
||||||
|
styleUrls?: string[];
|
||||||
|
interpolation?: [string, string];
|
||||||
|
encapsulation?: ViewEncapsulation;
|
||||||
|
animations?: CompileAnimationEntryMetadata[];
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DirectiveNormalizer {
|
export class DirectiveNormalizer {
|
||||||
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
private _resourceLoaderCache = new Map<string, Promise<string>>();
|
||||||
@ -41,7 +52,7 @@ export class DirectiveNormalizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _fetch(url: string): Promise<string> {
|
private _fetch(url: string): Promise<string> {
|
||||||
var result = this._resourceLoaderCache.get(url);
|
let result = this._resourceLoaderCache.get(url);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this._resourceLoader.get(url);
|
result = this._resourceLoader.get(url);
|
||||||
this._resourceLoaderCache.set(url, result);
|
this._resourceLoaderCache.set(url, result);
|
||||||
@ -49,65 +60,56 @@ export class DirectiveNormalizer {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeDirective(directive: CompileDirectiveMetadata):
|
normalizeTemplate(prenormData: PrenormalizedTemplateMetadata):
|
||||||
SyncAsyncResult<CompileDirectiveMetadata> {
|
SyncAsyncResult<CompileTemplateMetadata> {
|
||||||
if (!directive.isComponent) {
|
|
||||||
// For non components there is nothing to be normalized yet.
|
|
||||||
return new SyncAsyncResult(directive, Promise.resolve(directive));
|
|
||||||
}
|
|
||||||
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
||||||
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
||||||
if (isPresent(directive.template.template)) {
|
if (isPresent(prenormData.template)) {
|
||||||
normalizedTemplateSync = this.normalizeTemplateSync(directive.type, directive.template);
|
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
|
||||||
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
||||||
} else if (directive.template.templateUrl) {
|
} else if (prenormData.templateUrl) {
|
||||||
normalizedTemplateAsync = this.normalizeTemplateAsync(directive.type, directive.template);
|
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`No template specified for component ${directive.type.name}`);
|
throw new Error(
|
||||||
|
`No template specified for component ${stringify(prenormData.componentType)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
|
if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
|
||||||
// sync case
|
// sync case
|
||||||
let normalizedDirective = _cloneDirectiveWithTemplate(directive, normalizedTemplateSync);
|
return new SyncAsyncResult(normalizedTemplateSync);
|
||||||
return new SyncAsyncResult(normalizedDirective, Promise.resolve(normalizedDirective));
|
|
||||||
} else {
|
} else {
|
||||||
// async case
|
// async case
|
||||||
return new SyncAsyncResult(
|
return new SyncAsyncResult(
|
||||||
null,
|
null, normalizedTemplateAsync.then(
|
||||||
normalizedTemplateAsync
|
(normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate)));
|
||||||
.then((normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate))
|
|
||||||
.then(
|
|
||||||
(normalizedTemplate) =>
|
|
||||||
_cloneDirectiveWithTemplate(directive, normalizedTemplate)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTemplateSync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
normalizeTemplateSync(prenomData: PrenormalizedTemplateMetadata): CompileTemplateMetadata {
|
||||||
CompileTemplateMetadata {
|
return this.normalizeLoadedTemplate(prenomData, prenomData.template, prenomData.moduleUrl);
|
||||||
return this.normalizeLoadedTemplate(
|
|
||||||
directiveType, template, template.template, directiveType.moduleUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTemplateAsync(directiveType: CompileTypeMetadata, template: CompileTemplateMetadata):
|
normalizeTemplateAsync(prenomData: PrenormalizedTemplateMetadata):
|
||||||
Promise<CompileTemplateMetadata> {
|
Promise<CompileTemplateMetadata> {
|
||||||
let templateUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl);
|
const templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl);
|
||||||
return this._fetch(templateUrl)
|
return this._fetch(templateUrl)
|
||||||
.then((value) => this.normalizeLoadedTemplate(directiveType, template, value, templateUrl));
|
.then((value) => this.normalizeLoadedTemplate(prenomData, value, templateUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeLoadedTemplate(
|
normalizeLoadedTemplate(
|
||||||
directiveType: CompileTypeMetadata, templateMeta: CompileTemplateMetadata, template: string,
|
prenomData: PrenormalizedTemplateMetadata, template: string,
|
||||||
templateAbsUrl: string): CompileTemplateMetadata {
|
templateAbsUrl: string): CompileTemplateMetadata {
|
||||||
const interpolationConfig = InterpolationConfig.fromArray(templateMeta.interpolation);
|
const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation);
|
||||||
const rootNodesAndErrors =
|
const rootNodesAndErrors = this._htmlParser.parse(
|
||||||
this._htmlParser.parse(template, directiveType.name, false, interpolationConfig);
|
template, stringify(prenomData.componentType), false, interpolationConfig);
|
||||||
if (rootNodesAndErrors.errors.length > 0) {
|
if (rootNodesAndErrors.errors.length > 0) {
|
||||||
const errorString = rootNodesAndErrors.errors.join('\n');
|
const errorString = rootNodesAndErrors.errors.join('\n');
|
||||||
throw new Error(`Template parse errors:\n${errorString}`);
|
throw new Error(`Template parse errors:\n${errorString}`);
|
||||||
}
|
}
|
||||||
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
||||||
styles: templateMeta.styles,
|
styles: prenomData.styles,
|
||||||
styleUrls: templateMeta.styleUrls,
|
styleUrls: prenomData.styleUrls,
|
||||||
moduleUrl: directiveType.moduleUrl
|
moduleUrl: prenomData.moduleUrl
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const visitor = new TemplatePreparseVisitor();
|
const visitor = new TemplatePreparseVisitor();
|
||||||
@ -115,7 +117,7 @@ export class DirectiveNormalizer {
|
|||||||
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
||||||
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
||||||
|
|
||||||
let encapsulation = templateMeta.encapsulation;
|
let encapsulation = prenomData.encapsulation;
|
||||||
if (isBlank(encapsulation)) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = this._config.defaultEncapsulation;
|
encapsulation = this._config.defaultEncapsulation;
|
||||||
}
|
}
|
||||||
@ -132,10 +134,9 @@ export class DirectiveNormalizer {
|
|||||||
encapsulation,
|
encapsulation,
|
||||||
template,
|
template,
|
||||||
templateUrl: templateAbsUrl, styles, styleUrls,
|
templateUrl: templateAbsUrl, styles, styleUrls,
|
||||||
externalStylesheets: templateMeta.externalStylesheets,
|
|
||||||
ngContentSelectors: visitor.ngContentSelectors,
|
ngContentSelectors: visitor.ngContentSelectors,
|
||||||
animations: templateMeta.animations,
|
animations: prenomData.animations,
|
||||||
interpolation: templateMeta.interpolation,
|
interpolation: prenomData.interpolation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,21 +164,21 @@ export class DirectiveNormalizer {
|
|||||||
return Promise
|
return Promise
|
||||||
.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
||||||
.map(styleUrl => this._fetch(styleUrl).then((loadedStyle) => {
|
.map(styleUrl => this._fetch(styleUrl).then((loadedStyle) => {
|
||||||
var stylesheet = this.normalizeStylesheet(
|
const stylesheet = this.normalizeStylesheet(
|
||||||
new CompileStylesheetMetadata({styles: [loadedStyle], moduleUrl: styleUrl}));
|
new CompileStylesheetMetadata({styles: [loadedStyle], moduleUrl: styleUrl}));
|
||||||
loadedStylesheets.set(styleUrl, stylesheet);
|
loadedStylesheets.set(styleUrl, stylesheet);
|
||||||
return this._loadMissingExternalStylesheets(
|
return this._loadMissingExternalStylesheets(
|
||||||
stylesheet.styleUrls, loadedStylesheets);
|
stylesheet.styleUrls, loadedStylesheets);
|
||||||
})))
|
})))
|
||||||
.then((_) => MapWrapper.values(loadedStylesheets));
|
.then((_) => Array.from(loadedStylesheets.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata {
|
normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata {
|
||||||
var allStyleUrls = stylesheet.styleUrls.filter(isStyleUrlResolvable)
|
const allStyleUrls = stylesheet.styleUrls.filter(isStyleUrlResolvable)
|
||||||
.map(url => this._urlResolver.resolve(stylesheet.moduleUrl, url));
|
.map(url => this._urlResolver.resolve(stylesheet.moduleUrl, url));
|
||||||
|
|
||||||
var allStyles = stylesheet.styles.map(style => {
|
const allStyles = stylesheet.styles.map(style => {
|
||||||
var styleWithImports = extractStyleUrls(this._urlResolver, stylesheet.moduleUrl, style);
|
const styleWithImports = extractStyleUrls(this._urlResolver, stylesheet.moduleUrl, style);
|
||||||
allStyleUrls.push(...styleWithImports.styleUrls);
|
allStyleUrls.push(...styleWithImports.styleUrls);
|
||||||
return styleWithImports.style;
|
return styleWithImports.style;
|
||||||
});
|
});
|
||||||
@ -194,7 +195,7 @@ class TemplatePreparseVisitor implements html.Visitor {
|
|||||||
ngNonBindableStackCount: number = 0;
|
ngNonBindableStackCount: number = 0;
|
||||||
|
|
||||||
visitElement(ast: html.Element, context: any): any {
|
visitElement(ast: html.Element, context: any): any {
|
||||||
var preparsedElement = preparseElement(ast);
|
const preparsedElement = preparseElement(ast);
|
||||||
switch (preparsedElement.type) {
|
switch (preparsedElement.type) {
|
||||||
case PreparsedElementType.NG_CONTENT:
|
case PreparsedElementType.NG_CONTENT:
|
||||||
if (this.ngNonBindableStackCount === 0) {
|
if (this.ngNonBindableStackCount === 0) {
|
||||||
@ -202,7 +203,7 @@ class TemplatePreparseVisitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PreparsedElementType.STYLE:
|
case PreparsedElementType.STYLE:
|
||||||
var textContent = '';
|
let textContent = '';
|
||||||
ast.children.forEach(child => {
|
ast.children.forEach(child => {
|
||||||
if (child instanceof html.Text) {
|
if (child instanceof html.Text) {
|
||||||
textContent += child.value;
|
textContent += child.value;
|
||||||
@ -232,25 +233,3 @@ class TemplatePreparseVisitor implements html.Visitor {
|
|||||||
visitExpansion(ast: html.Expansion, context: any): any { return null; }
|
visitExpansion(ast: html.Expansion, context: any): any { return null; }
|
||||||
visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; }
|
visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _cloneDirectiveWithTemplate(
|
|
||||||
directive: CompileDirectiveMetadata,
|
|
||||||
template: CompileTemplateMetadata): CompileDirectiveMetadata {
|
|
||||||
return new CompileDirectiveMetadata({
|
|
||||||
type: directive.type,
|
|
||||||
isComponent: directive.isComponent,
|
|
||||||
selector: directive.selector,
|
|
||||||
exportAs: directive.exportAs,
|
|
||||||
changeDetection: directive.changeDetection,
|
|
||||||
inputs: directive.inputs,
|
|
||||||
outputs: directive.outputs,
|
|
||||||
hostListeners: directive.hostListeners,
|
|
||||||
hostProperties: directive.hostProperties,
|
|
||||||
hostAttributes: directive.hostAttributes,
|
|
||||||
providers: directive.providers,
|
|
||||||
viewProviders: directive.viewProviders,
|
|
||||||
queries: directive.queries,
|
|
||||||
viewQueries: directive.viewQueries,
|
|
||||||
entryComponents: directive.entryComponents, template,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -24,6 +24,11 @@ import {splitAtColon} from './util';
|
|||||||
export class DirectiveResolver {
|
export class DirectiveResolver {
|
||||||
constructor(private _reflector: ReflectorReader = reflector) {}
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
||||||
|
|
||||||
|
isDirective(type: Type<any>) {
|
||||||
|
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||||
|
return typeMetadata && typeMetadata.some(isDirectiveMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {@link Directive} for a given `Type`.
|
* Return {@link Directive} for a given `Type`.
|
||||||
*/
|
*/
|
||||||
@ -116,7 +121,7 @@ export class DirectiveResolver {
|
|||||||
mergedInputs.unshift(...directive.inputs);
|
mergedInputs.unshift(...directive.inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mergedOutputs: string[] = outputs;
|
const mergedOutputs: string[] = outputs;
|
||||||
|
|
||||||
if (directive.outputs) {
|
if (directive.outputs) {
|
||||||
const outputNames: string[] =
|
const outputNames: string[] =
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata} from './compile_metadata';
|
||||||
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
||||||
import {convertPropertyBinding} from './compiler_util/expression_converter';
|
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||||
import {writeToRenderer} from './compiler_util/render_util';
|
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {Parser} from './expression_parser/parser';
|
import {Parser} from './expression_parser/parser';
|
||||||
import {Identifiers, resolveIdentifier} from './identifiers';
|
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||||
@ -29,14 +29,17 @@ export class DirectiveWrapperCompileResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CONTEXT_FIELD_NAME = 'context';
|
const CONTEXT_FIELD_NAME = 'context';
|
||||||
const CHANGES_FIELD_NAME = 'changes';
|
const CHANGES_FIELD_NAME = '_changes';
|
||||||
const CHANGED_FIELD_NAME = 'changed';
|
const CHANGED_FIELD_NAME = '_changed';
|
||||||
|
const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
|
||||||
|
|
||||||
const CURR_VALUE_VAR = o.variable('currValue');
|
const CURR_VALUE_VAR = o.variable('currValue');
|
||||||
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
|
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
|
||||||
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
|
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
|
||||||
const VIEW_VAR = o.variable('view');
|
const VIEW_VAR = o.variable('view');
|
||||||
|
const COMPONENT_VIEW_VAR = o.variable('componentView');
|
||||||
const RENDER_EL_VAR = o.variable('el');
|
const RENDER_EL_VAR = o.variable('el');
|
||||||
|
const EVENT_NAME_VAR = o.variable('eventName');
|
||||||
|
|
||||||
const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap([])).toStmt();
|
const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap([])).toStmt();
|
||||||
|
|
||||||
@ -57,22 +60,17 @@ export class DirectiveWrapperCompiler {
|
|||||||
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
|
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
|
||||||
|
|
||||||
compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult {
|
compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult {
|
||||||
|
const hostParseResult = parseHostBindings(dirMeta, this._exprParser, this._schemaRegistry);
|
||||||
|
reportParseErrors(hostParseResult.errors, this._console);
|
||||||
|
|
||||||
const builder = new DirectiveWrapperBuilder(this.compilerConfig, dirMeta);
|
const builder = new DirectiveWrapperBuilder(this.compilerConfig, dirMeta);
|
||||||
Object.keys(dirMeta.inputs).forEach((inputFieldName) => {
|
Object.keys(dirMeta.inputs).forEach((inputFieldName) => {
|
||||||
addCheckInputMethod(inputFieldName, builder);
|
addCheckInputMethod(inputFieldName, builder);
|
||||||
});
|
});
|
||||||
addDetectChangesInInputPropsMethod(builder);
|
addNgDoCheckMethod(builder);
|
||||||
|
addCheckHostMethod(hostParseResult.hostProps, builder);
|
||||||
const hostParseResult = parseHostBindings(dirMeta, this._exprParser, this._schemaRegistry);
|
addHandleEventMethod(hostParseResult.hostListeners, builder);
|
||||||
reportParseErrors(hostParseResult.errors, this._console);
|
addSubscribeMethod(dirMeta, builder);
|
||||||
// host properties are change detected by the DirectiveWrappers,
|
|
||||||
// except for the animation properties as they need close integration with animation events
|
|
||||||
// and DirectiveWrappers don't support
|
|
||||||
// event listeners right now.
|
|
||||||
addDetectChangesInHostPropsMethod(
|
|
||||||
hostParseResult.hostProps.filter(hostProp => !hostProp.isAnimation), builder);
|
|
||||||
|
|
||||||
// TODO(tbosch): implement hostListeners via DirectiveWrapper as well!
|
|
||||||
|
|
||||||
const classStmt = builder.build();
|
const classStmt = builder.build();
|
||||||
return new DirectiveWrapperCompileResult([classStmt], classStmt.name);
|
return new DirectiveWrapperCompileResult([classStmt], classStmt.name);
|
||||||
@ -84,11 +82,14 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
getters: o.ClassGetter[] = [];
|
getters: o.ClassGetter[] = [];
|
||||||
methods: o.ClassMethod[] = [];
|
methods: o.ClassMethod[] = [];
|
||||||
ctorStmts: o.Statement[] = [];
|
ctorStmts: o.Statement[] = [];
|
||||||
|
detachStmts: o.Statement[] = [];
|
||||||
|
destroyStmts: o.Statement[] = [];
|
||||||
|
|
||||||
genChanges: boolean;
|
genChanges: boolean;
|
||||||
ngOnChanges: boolean;
|
ngOnChanges: boolean;
|
||||||
ngOnInit: boolean;
|
ngOnInit: boolean;
|
||||||
ngDoCheck: boolean;
|
ngDoCheck: boolean;
|
||||||
|
ngOnDestroy: boolean;
|
||||||
|
|
||||||
constructor(public compilerConfig: CompilerConfig, public dirMeta: CompileDirectiveMetadata) {
|
constructor(public compilerConfig: CompilerConfig, public dirMeta: CompileDirectiveMetadata) {
|
||||||
const dirLifecycleHooks = dirMeta.type.lifecycleHooks;
|
const dirLifecycleHooks = dirMeta.type.lifecycleHooks;
|
||||||
@ -97,6 +98,11 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
this.ngOnChanges = dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
|
this.ngOnChanges = dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
|
||||||
this.ngOnInit = dirLifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1;
|
this.ngOnInit = dirLifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1;
|
||||||
this.ngDoCheck = dirLifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1;
|
this.ngDoCheck = dirLifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1;
|
||||||
|
this.ngOnDestroy = dirLifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1;
|
||||||
|
if (this.ngOnDestroy) {
|
||||||
|
this.destroyStmts.push(
|
||||||
|
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnDestroy', []).toStmt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
build(): o.ClassStmt {
|
build(): o.ClassStmt {
|
||||||
@ -105,14 +111,33 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
dirDepParamNames.push(`p${i}`);
|
dirDepParamNames.push(`p${i}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const methods = [
|
||||||
|
new o.ClassMethod(
|
||||||
|
'ngOnDetach',
|
||||||
|
[
|
||||||
|
new o.FnParam(
|
||||||
|
VIEW_VAR.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(
|
||||||
|
COMPONENT_VIEW_VAR.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
],
|
||||||
|
this.detachStmts),
|
||||||
|
new o.ClassMethod('ngOnDestroy', [], this.destroyStmts),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
const fields: o.ClassField[] = [
|
const fields: o.ClassField[] = [
|
||||||
|
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
|
||||||
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
|
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
|
||||||
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE),
|
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
|
||||||
];
|
];
|
||||||
const ctorStmts: o.Statement[] =
|
const ctorStmts: o.Statement[] =
|
||||||
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
|
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
|
||||||
if (this.genChanges) {
|
if (this.genChanges) {
|
||||||
fields.push(new o.ClassField(CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE)));
|
fields.push(new o.ClassField(
|
||||||
|
CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE), [o.StmtModifier.Private]));
|
||||||
ctorStmts.push(RESET_CHANGES_STMT);
|
ctorStmts.push(RESET_CHANGES_STMT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,12 +150,12 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
return createClassStmt({
|
return createClassStmt({
|
||||||
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
|
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
|
||||||
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
|
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
|
||||||
builders: [{fields, ctorStmts}, this]
|
builders: [{fields, ctorStmts, methods}, this]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDetectChangesInInputPropsMethod(builder: DirectiveWrapperBuilder) {
|
function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
||||||
const changedVar = o.variable('changed');
|
const changedVar = o.variable('changed');
|
||||||
const stmts: o.Statement[] = [
|
const stmts: o.Statement[] = [
|
||||||
changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(),
|
changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(),
|
||||||
@ -170,7 +195,7 @@ function addDetectChangesInInputPropsMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
stmts.push(new o.ReturnStatement(changedVar));
|
stmts.push(new o.ReturnStatement(changedVar));
|
||||||
|
|
||||||
builder.methods.push(new o.ClassMethod(
|
builder.methods.push(new o.ClassMethod(
|
||||||
'detectChangesInInputProps',
|
'ngDoCheck',
|
||||||
[
|
[
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
@ -182,7 +207,7 @@ function addDetectChangesInInputPropsMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
|
|
||||||
function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
||||||
const field = createCheckBindingField(builder);
|
const field = createCheckBindingField(builder);
|
||||||
var onChangeStatements: o.Statement[] = [
|
const onChangeStatements: o.Statement[] = [
|
||||||
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
|
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
|
||||||
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
|
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
|
||||||
];
|
];
|
||||||
@ -194,7 +219,7 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
|||||||
.toStmt());
|
.toStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
var methodBody: o.Statement[] = createCheckBindingStmt(
|
const methodBody: o.Statement[] = createCheckBindingStmt(
|
||||||
{currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression,
|
{currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression,
|
||||||
THROW_ON_CHANGE_VAR, onChangeStatements);
|
THROW_ON_CHANGE_VAR, onChangeStatements);
|
||||||
builder.methods.push(new o.ClassMethod(
|
builder.methods.push(new o.ClassMethod(
|
||||||
@ -207,16 +232,19 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
|||||||
methodBody));
|
methodBody));
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDetectChangesInHostPropsMethod(
|
function addCheckHostMethod(
|
||||||
hostProps: BoundElementPropertyAst[], builder: DirectiveWrapperBuilder) {
|
hostProps: BoundElementPropertyAst[], builder: DirectiveWrapperBuilder) {
|
||||||
const stmts: o.Statement[] = [];
|
const stmts: o.Statement[] = [];
|
||||||
const methodParams: o.FnParam[] = [
|
const methodParams: o.FnParam[] = [
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(
|
||||||
|
COMPONENT_VIEW_VAR.name,
|
||||||
|
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
];
|
];
|
||||||
hostProps.forEach((hostProp) => {
|
hostProps.forEach((hostProp, hostPropIdx) => {
|
||||||
const field = createCheckBindingField(builder);
|
const field = createCheckBindingField(builder);
|
||||||
const evalResult = convertPropertyBinding(
|
const evalResult = convertPropertyBinding(
|
||||||
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostProp.value, field.bindingId);
|
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostProp.value, field.bindingId);
|
||||||
@ -229,13 +257,83 @@ function addDetectChangesInHostPropsMethod(
|
|||||||
methodParams.push(new o.FnParam(
|
methodParams.push(new o.FnParam(
|
||||||
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext))));
|
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext))));
|
||||||
}
|
}
|
||||||
|
let checkBindingStmts: o.Statement[];
|
||||||
|
if (hostProp.isAnimation) {
|
||||||
|
const {updateStmts, detachStmts} = triggerAnimation(
|
||||||
|
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp,
|
||||||
|
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME)
|
||||||
|
.or(o.importExpr(resolveIdentifier(Identifiers.noop))),
|
||||||
|
RENDER_EL_VAR, evalResult.currValExpr, field.expression);
|
||||||
|
checkBindingStmts = updateStmts;
|
||||||
|
builder.detachStmts.push(...detachStmts);
|
||||||
|
} else {
|
||||||
|
checkBindingStmts = writeToRenderer(
|
||||||
|
VIEW_VAR, hostProp, RENDER_EL_VAR, evalResult.currValExpr,
|
||||||
|
builder.compilerConfig.logBindingUpdate, securityContextExpr);
|
||||||
|
}
|
||||||
|
|
||||||
stmts.push(...createCheckBindingStmt(
|
stmts.push(...createCheckBindingStmt(
|
||||||
evalResult, field.expression, THROW_ON_CHANGE_VAR,
|
evalResult, field.expression, THROW_ON_CHANGE_VAR, checkBindingStmts));
|
||||||
writeToRenderer(
|
|
||||||
VIEW_VAR, hostProp, RENDER_EL_VAR, evalResult.currValExpr,
|
|
||||||
builder.compilerConfig.logBindingUpdate, securityContextExpr)));
|
|
||||||
});
|
});
|
||||||
builder.methods.push(new o.ClassMethod('detectChangesInHostProps', methodParams, stmts));
|
builder.methods.push(new o.ClassMethod('checkHost', methodParams, stmts));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHandleEventMethod(hostListeners: BoundEventAst[], builder: DirectiveWrapperBuilder) {
|
||||||
|
const resultVar = o.variable(`result`);
|
||||||
|
const actionStmts: o.Statement[] = [resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)];
|
||||||
|
hostListeners.forEach((hostListener, eventIdx) => {
|
||||||
|
const evalResult = convertActionBinding(
|
||||||
|
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler,
|
||||||
|
`sub_${eventIdx}`);
|
||||||
|
const trueStmts = evalResult.stmts;
|
||||||
|
if (evalResult.preventDefault) {
|
||||||
|
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
|
||||||
|
}
|
||||||
|
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
|
||||||
|
actionStmts.push(
|
||||||
|
new o.IfStmt(EVENT_NAME_VAR.equals(o.literal(hostListener.fullName)), trueStmts));
|
||||||
|
});
|
||||||
|
actionStmts.push(new o.ReturnStatement(resultVar));
|
||||||
|
builder.methods.push(new o.ClassMethod(
|
||||||
|
'handleEvent',
|
||||||
|
[
|
||||||
|
new o.FnParam(EVENT_NAME_VAR.name, o.STRING_TYPE),
|
||||||
|
new o.FnParam(EventHandlerVars.event.name, o.DYNAMIC_TYPE)
|
||||||
|
],
|
||||||
|
actionStmts, o.BOOL_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
|
||||||
|
const methodParams: o.FnParam[] = [
|
||||||
|
new o.FnParam(
|
||||||
|
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
|
new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)
|
||||||
|
];
|
||||||
|
const stmts: o.Statement[] = [
|
||||||
|
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME).set(o.variable(EVENT_HANDLER_FIELD_NAME)).toStmt()
|
||||||
|
];
|
||||||
|
Object.keys(dirMeta.outputs).forEach((emitterPropName, emitterIdx) => {
|
||||||
|
const eventName = dirMeta.outputs[emitterPropName];
|
||||||
|
const paramName = `emit${emitterIdx}`;
|
||||||
|
methodParams.push(new o.FnParam(paramName, o.BOOL_TYPE));
|
||||||
|
const subscriptionFieldName = `subscription${emitterIdx}`;
|
||||||
|
builder.fields.push(new o.ClassField(subscriptionFieldName, o.DYNAMIC_TYPE));
|
||||||
|
stmts.push(new o.IfStmt(o.variable(paramName), [
|
||||||
|
o.THIS_EXPR.prop(subscriptionFieldName)
|
||||||
|
.set(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
|
||||||
|
.prop(emitterPropName)
|
||||||
|
.callMethod(
|
||||||
|
o.BuiltinMethod.SubscribeObservable,
|
||||||
|
[o.variable(EVENT_HANDLER_FIELD_NAME)
|
||||||
|
.callMethod(o.BuiltinMethod.Bind, [VIEW_VAR, o.literal(eventName)])]))
|
||||||
|
.toStmt()
|
||||||
|
]));
|
||||||
|
builder.destroyStmts.push(
|
||||||
|
o.THIS_EXPR.prop(subscriptionFieldName)
|
||||||
|
.and(o.THIS_EXPR.prop(subscriptionFieldName).callMethod('unsubscribe', []))
|
||||||
|
.toStmt());
|
||||||
|
});
|
||||||
|
builder.methods.push(new o.ClassMethod('subscribe', methodParams, stmts));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ParseResult {
|
class ParseResult {
|
||||||
@ -257,8 +355,8 @@ function parseHostBindings(
|
|||||||
const sourceSpan = new ParseSourceSpan(
|
const sourceSpan = new ParseSourceSpan(
|
||||||
new ParseLocation(sourceFile, null, null, null),
|
new ParseLocation(sourceFile, null, null, null),
|
||||||
new ParseLocation(sourceFile, null, null, null));
|
new ParseLocation(sourceFile, null, null, null));
|
||||||
const parsedHostProps = parser.createDirectiveHostPropertyAsts(dirMeta, sourceSpan);
|
const parsedHostProps = parser.createDirectiveHostPropertyAsts(dirMeta.toSummary(), sourceSpan);
|
||||||
const parsedHostListeners = parser.createDirectiveHostEventAsts(dirMeta, sourceSpan);
|
const parsedHostListeners = parser.createDirectiveHostEventAsts(dirMeta.toSummary(), sourceSpan);
|
||||||
|
|
||||||
return new ParseResult(parsedHostProps, parsedHostListeners, errors);
|
return new ParseResult(parsedHostProps, parsedHostListeners, errors);
|
||||||
}
|
}
|
||||||
@ -274,4 +372,87 @@ function reportParseErrors(parseErrors: ParseError[], console: Console) {
|
|||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
throw new Error(`Directive parse errors:\n${errors.join('\n')}`);
|
throw new Error(`Directive parse errors:\n${errors.join('\n')}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DirectiveWrapperExpressions {
|
||||||
|
static create(dir: CompileIdentifierMetadata, depsExpr: o.Expression[]): o.Expression {
|
||||||
|
return o.importExpr(dir).instantiate(depsExpr, o.importType(dir));
|
||||||
|
}
|
||||||
|
static context(dirWrapper: o.Expression): o.ReadPropExpr {
|
||||||
|
return dirWrapper.prop(CONTEXT_FIELD_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ngDoCheck(
|
||||||
|
dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression,
|
||||||
|
throwOnChange: o.Expression): o.Expression {
|
||||||
|
return dirWrapper.callMethod('ngDoCheck', [view, renderElement, throwOnChange]);
|
||||||
|
}
|
||||||
|
static checkHost(
|
||||||
|
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
|
||||||
|
componentView: o.Expression, renderElement: o.Expression, throwOnChange: o.Expression,
|
||||||
|
runtimeSecurityContexts: o.Expression[]): o.Statement[] {
|
||||||
|
if (hostProps.length) {
|
||||||
|
return [dirWrapper
|
||||||
|
.callMethod(
|
||||||
|
'checkHost', [view, componentView, renderElement, throwOnChange].concat(
|
||||||
|
runtimeSecurityContexts))
|
||||||
|
.toStmt()];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static ngOnDetach(
|
||||||
|
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
|
||||||
|
componentView: o.Expression, renderEl: o.Expression): o.Statement[] {
|
||||||
|
if (hostProps.some(prop => prop.isAnimation)) {
|
||||||
|
return [dirWrapper
|
||||||
|
.callMethod(
|
||||||
|
'ngOnDetach',
|
||||||
|
[
|
||||||
|
view,
|
||||||
|
componentView,
|
||||||
|
renderEl,
|
||||||
|
])
|
||||||
|
.toStmt()];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static ngOnDestroy(dir: CompileDirectiveSummary, dirWrapper: o.Expression): o.Statement[] {
|
||||||
|
if (dir.type.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1 ||
|
||||||
|
Object.keys(dir.outputs).length > 0) {
|
||||||
|
return [dirWrapper.callMethod('ngOnDestroy', []).toStmt()];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static subscribe(
|
||||||
|
dirMeta: CompileDirectiveSummary, hostProps: BoundElementPropertyAst[], usedEvents: string[],
|
||||||
|
dirWrapper: o.Expression, view: o.Expression, eventListener: o.Expression): o.Statement[] {
|
||||||
|
let needsSubscribe = false;
|
||||||
|
const eventFlags: o.Expression[] = [];
|
||||||
|
Object.keys(dirMeta.outputs).forEach((propName) => {
|
||||||
|
const eventName = dirMeta.outputs[propName];
|
||||||
|
const eventUsed = usedEvents.indexOf(eventName) > -1;
|
||||||
|
needsSubscribe = needsSubscribe || eventUsed;
|
||||||
|
eventFlags.push(o.literal(eventUsed));
|
||||||
|
});
|
||||||
|
hostProps.forEach((hostProp) => {
|
||||||
|
if (hostProp.isAnimation && usedEvents.length > 0) {
|
||||||
|
needsSubscribe = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (needsSubscribe) {
|
||||||
|
return [
|
||||||
|
dirWrapper.callMethod('subscribe', [view, eventListener].concat(eventFlags)).toStmt()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static handleEvent(
|
||||||
|
hostEvents: BoundEventAst[], dirWrapper: o.Expression, eventName: o.Expression,
|
||||||
|
event: o.Expression): o.Expression {
|
||||||
|
return dirWrapper.callMethod('handleEvent', [eventName, event]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user