Compare commits
2278 Commits
starting
...
2.0.0-alph
Author | SHA1 | Date | |
---|---|---|---|
9fc24b9c52 | |||
247479d017 | |||
57649d1839 | |||
e82a35d1fd | |||
50e922f37b | |||
3b036601cc | |||
e15e2428c5 | |||
3a801c16fa | |||
717bd23c37 | |||
d02b794af2 | |||
bb9d299b38 | |||
1bc35208df | |||
ea661fa10f | |||
c0b7bae9d3 | |||
8bc40d3f4d | |||
440fd11c72 | |||
f529236bfc | |||
5a504e6b78 | |||
a1fa2e472f | |||
eb2c15786e | |||
947bf11cb6 | |||
1aeafd31bd | |||
f443ecbfa6 | |||
8667b760f4 | |||
6abed8d996 | |||
eacc8e3803 | |||
da1272f368 | |||
0def28e594 | |||
b96784756c | |||
62005dd127 | |||
52ebb8f0dd | |||
35878c559f | |||
0c1f9dab54 | |||
1eb0162cde | |||
7c6130c2c5 | |||
df09389df8 | |||
90191ce261 | |||
19c1bd7375 | |||
84c3124292 | |||
cd90e6ed8f | |||
867c08ac84 | |||
95f984615b | |||
393b0526b4 | |||
6207b1af88 | |||
a8c34ae290 | |||
aab0c57aee | |||
c178ad476e | |||
597f79e0c7 | |||
31f48ae943 | |||
1272affe5c | |||
431ac33c26 | |||
cd2050b6e0 | |||
075011f2fe | |||
35a61b449a | |||
984b6fc487 | |||
1100c9b1e3 | |||
f3ab527688 | |||
bba0248989 | |||
bc8c194665 | |||
14fa00791e | |||
d60c7a9118 | |||
d63f3c123e | |||
8b725c77fd | |||
5458036de7 | |||
6075509f26 | |||
ee32b1bc37 | |||
6d4bd5d901 | |||
ff77230edb | |||
21b53c3143 | |||
04b3dee667 | |||
6b00b60488 | |||
bffab0f2db | |||
811d4c03bd | |||
af1119063c | |||
fcc6f2c561 | |||
c94f239536 | |||
5600a95e97 | |||
07572652ff | |||
aee176115b | |||
4ed642f921 | |||
a7b2ab771e | |||
a0277f1b3a | |||
aaa215514b | |||
b6537ad609 | |||
62e14dc529 | |||
15ab6f6ea8 | |||
dea6a4593b | |||
d38689677c | |||
a2e7ae568e | |||
bd31b01690 | |||
f7b75330e0 | |||
f7aa890ade | |||
4a36fd8203 | |||
ba6e0e11fa | |||
4ac29621f4 | |||
75187d605b | |||
5040a8e0df | |||
a4869c874b | |||
b6210be648 | |||
8e1d2fb325 | |||
bb4fd2de94 | |||
fd5cd84065 | |||
6117a7037b | |||
4d9de666c8 | |||
4ebb1a9096 | |||
9679fc94f6 | |||
076191cf9d | |||
74c0daab27 | |||
6093e28b61 | |||
ae6f549f10 | |||
74c852b160 | |||
349416ea53 | |||
f638834fcf | |||
09371a3f0b | |||
0b3e4fa090 | |||
0299d4af00 | |||
a9aef8e5e6 | |||
6fe8b85295 | |||
649d310c31 | |||
14b194ea21 | |||
758efba34b | |||
e21bf120b9 | |||
9fa57d06da | |||
9fc9704cdd | |||
39e9bb64ef | |||
a649992ccd | |||
2628631aff | |||
4b0013c7e7 | |||
5ff1eb75d0 | |||
1b20873433 | |||
36562ecaae | |||
fc764b42f6 | |||
9f688d314f | |||
150cc22532 | |||
208f3d4c65 | |||
7c4199cd1c | |||
2ebc74ddcc | |||
c7e9f86557 | |||
75cf0aeaeb | |||
decdbea7d7 | |||
01fb06a377 | |||
c9901c5fe0 | |||
5ea6dc844c | |||
b44c13bc46 | |||
6ae9686a0d | |||
4342a1eb7b | |||
13161ae5aa | |||
6b5d345a2b | |||
2450a3c5b0 | |||
34518f0f2d | |||
a251df9df4 | |||
a110ce95dc | |||
e8e57cdd73 | |||
aa3d94149f | |||
6248a5e846 | |||
9f4fa1ab0a | |||
43cca2de76 | |||
d21c7bdf90 | |||
b154f1a44f | |||
76247b7097 | |||
30ca0434a2 | |||
841f8789fd | |||
52236bd765 | |||
a5622304de | |||
074e289e62 | |||
617acf4cd6 | |||
a87ebb28e2 | |||
adbfd29fd7 | |||
33593cf8a2 | |||
00a4b2e28f | |||
7b2d8fce07 | |||
cec4b36d9b | |||
5557a5716d | |||
eafa482052 | |||
3aa204791b | |||
9b7378d132 | |||
fb9796130d | |||
e1853b33d3 | |||
4731714244 | |||
b4fe590b2d | |||
8ff65a30c7 | |||
cd0e9c9cd4 | |||
a7b8624b55 | |||
7ae74cf91d | |||
6a6396963f | |||
4319563bba | |||
e2f5d87ff7 | |||
7714d6a6eb | |||
1f2302e39e | |||
a88e6f3106 | |||
3dd9919bbd | |||
589ce31dfc | |||
e7d65ad96f | |||
af9f916a9c | |||
d348e5051f | |||
7771ef4873 | |||
7ee295ad94 | |||
0319417a1b | |||
5bf6a3af15 | |||
7a53f82516 | |||
2ee32fb02c | |||
1cf45757cd | |||
0ed6fc4f6b | |||
9c9769047d | |||
5f640c79bb | |||
0a88e7b736 | |||
711ab6d573 | |||
4e5c663b02 | |||
4efc4a5520 | |||
ce6b364dc5 | |||
ef61b81b0c | |||
225eab8f25 | |||
7c2c1a8e03 | |||
9d2d674ef8 | |||
7470ad1bd1 | |||
bffa2cb59b | |||
4f56a01b3b | |||
29f75c3c63 | |||
ea15d6646b | |||
44c171e399 | |||
4087e3115b | |||
4d4d20edb9 | |||
3b609fbae9 | |||
8427863bab | |||
db6d289d82 | |||
ec68074e69 | |||
3d6e3c2551 | |||
0366f317af | |||
8a2370750a | |||
c01f327194 | |||
5c0a9eff62 | |||
9c4ab2b2b8 | |||
5ea9c1e7fd | |||
662a585fc2 | |||
ae3713ef95 | |||
707e6f7671 | |||
a554f1c45d | |||
41b019f5f8 | |||
af2cd4d6f3 | |||
577ee3744a | |||
9dd32d658e | |||
fd3eae9623 | |||
238dfc1e17 | |||
29b56ceb40 | |||
820b30c181 | |||
cc0c30484f | |||
eb7839e0ec | |||
f600d88209 | |||
662ff47627 | |||
da6def3772 | |||
c868f40bef | |||
17af481134 | |||
b2695e46e1 | |||
b0effe8e27 | |||
794e3870c5 | |||
95c8eef97a | |||
b8c4d3e7fb | |||
aed34e1f82 | |||
5205a9e65f | |||
6e0ca7f39a | |||
43f97a951c | |||
a443ea64a1 | |||
c2a60f1624 | |||
2e9de0b169 | |||
5dbe292615 | |||
5809a02624 | |||
3525d8a394 | |||
dd9b3b4ed0 | |||
283415beab | |||
390aacd442 | |||
e166f6fe95 | |||
5a3ce87915 | |||
2acc1ad08b | |||
b9ea394c85 | |||
06f8330cfa | |||
f490565b09 | |||
d05df30a94 | |||
e1c0b1abcb | |||
b2e9da4905 | |||
be6d92c29a | |||
9179afb2a1 | |||
adb3813e85 | |||
563fffd9b0 | |||
a588d4894b | |||
dad40751d4 | |||
19274e744d | |||
608f35b4a7 | |||
457b689bf0 | |||
eaa20f661a | |||
626e1cda5f | |||
34aa1425b7 | |||
241632aaa1 | |||
06f0d6c046 | |||
d276370ba1 | |||
d9776b4112 | |||
53412a71e5 | |||
70586b668c | |||
8f985dd558 | |||
64e8f93f32 | |||
cc1d758eba | |||
045cb90a46 | |||
d6cda15879 | |||
73351ac00f | |||
5bab607f44 | |||
16bf335a4a | |||
7b3161a229 | |||
267121ee17 | |||
045cc8269f | |||
9f999dd8e4 | |||
d890c4f827 | |||
c2043ec681 | |||
0808eeaa0c | |||
bced3aaa17 | |||
105db02e07 | |||
61b6a475a6 | |||
6db9f9013d | |||
f7d46e7159 | |||
5b6d576f93 | |||
255ed69392 | |||
851b1cf013 | |||
dcd2df2486 | |||
5f15363dab | |||
a15b679494 | |||
db098650ee | |||
f7b6ff0520 | |||
df7f59b3a8 | |||
abc4ef31e2 | |||
55290b9b21 | |||
d6464633fa | |||
c83207f231 | |||
3a7b50f244 | |||
e889ec8335 | |||
cb4ff7491a | |||
5298eb0709 | |||
cb4a9a3c04 | |||
97d1844bfc | |||
193792c27f | |||
4f57990d45 | |||
9dc1d6ae81 | |||
e4f94f0678 | |||
bfbce542e6 | |||
d2458866c1 | |||
cac25fe003 | |||
309944931f | |||
63e785902f | |||
83715f06d1 | |||
b42b9fc42d | |||
39ce9d3397 | |||
effbb54f3d | |||
d1dbc973c4 | |||
feddefdc84 | |||
787d1f955f | |||
0246b2a2cb | |||
71cbb49672 | |||
67c79ba3f6 | |||
d9036c6cf3 | |||
acc2722cb8 | |||
5e49d7e624 | |||
4c2fb1f6e8 | |||
de9dc55852 | |||
013811c171 | |||
e34a29dafe | |||
9b009b3159 | |||
d4383af022 | |||
b91a966d05 | |||
958863d038 | |||
fd14460148 | |||
4df0604f09 | |||
a826f22698 | |||
7ccef08715 | |||
716ce08a93 | |||
646b9200a2 | |||
6449c3c1c6 | |||
1fba78b12a | |||
80a04b4323 | |||
41c7c57c65 | |||
f7fb5097b9 | |||
20fbbcc72d | |||
f94a2d86fb | |||
55358a39dc | |||
a2bcf71b6b | |||
18de0ceeb1 | |||
fc82700364 | |||
16ecc7e82d | |||
ed586f0b4a | |||
f66c3a21eb | |||
17eb41ff48 | |||
31e385c70d | |||
cc8a6ebf68 | |||
19408968f3 | |||
efaed076ab | |||
47e09a38e6 | |||
ffd4f06ab7 | |||
3c13f5fd4b | |||
f6cc573687 | |||
892d2b9652 | |||
8d538ff42e | |||
63141ab9ab | |||
6a4a8a60ab | |||
7ec4da40ce | |||
4fd9cc26d5 | |||
1926335b85 | |||
12dd44f7f6 | |||
2daf2eedb6 | |||
2ab8c59ad5 | |||
0653b82048 | |||
77e8304fc4 | |||
8aec215ca0 | |||
f91c087c46 | |||
39a6f85e95 | |||
3dfb7d406b | |||
8810f53435 | |||
998c7c2e03 | |||
5e013c4aef | |||
8dd6c4680b | |||
28a29f5557 | |||
f3da37c92f | |||
3b9c08676a | |||
a8bdb693b9 | |||
5a59e8be82 | |||
26a064ce87 | |||
70f6a46f1c | |||
e36966b83c | |||
f14b212dc9 | |||
6d13cf9b8f | |||
841aa1af5b | |||
2d4f331c63 | |||
86bda288bd | |||
3d38ec8aac | |||
ef88e0f804 | |||
8302afffb4 | |||
67b9414268 | |||
d8c5ab232c | |||
c320240086 | |||
15164a8e6c | |||
4ec4dcabe9 | |||
e9ad100b1f | |||
b025f94351 | |||
df8e15cab7 | |||
855cb16cc7 | |||
5ebeaf7c9b | |||
9d42b52d2c | |||
34b91c62c5 | |||
4bffd97edd | |||
be954115f8 | |||
2384082b5c | |||
2a126f72f3 | |||
896add7d77 | |||
337ce21149 | |||
d78261695b | |||
ad3b9cf232 | |||
34deda594f | |||
f6108c54ec | |||
687e7b565f | |||
ddde7117a7 | |||
44c303aad2 | |||
53d0861372 | |||
696edde17c | |||
3ff321475d | |||
d43bd9b4ca | |||
79994b2abf | |||
7736964a37 | |||
2fea0c2602 | |||
5c9613e084 | |||
01cdd31339 | |||
44a991e245 | |||
46f751bd2f | |||
358908e605 | |||
6532171997 | |||
b9cf945b30 | |||
5b8ce1e42a | |||
7c7888de4f | |||
42e1b07705 | |||
60ce884671 | |||
9934b3ec7f | |||
12a8064c27 | |||
ad1bd5fc11 | |||
fc7068550c | |||
a8028b87c4 | |||
78f11f6333 | |||
3791c4a682 | |||
ecf6ba3974 | |||
b29b045d78 | |||
c349bbbc08 | |||
e916836261 | |||
4415855683 | |||
cebd670a8e | |||
b8be4bfaaf | |||
6c3c6060a5 | |||
46dd5fcbb0 | |||
0f54ed0306 | |||
9619636ba7 | |||
fa2c6791b4 | |||
ad16e9d910 | |||
7de447e4b5 | |||
36eb9d392d | |||
de37729823 | |||
e1a7e0329c | |||
76e1f863a2 | |||
d49bc438e8 | |||
f93cd9ced7 | |||
0f4eb1b524 | |||
3468f7cfd5 | |||
be07390859 | |||
551d9a1688 | |||
ac3f5106e4 | |||
37b042b361 | |||
465f7c95b0 | |||
69926dd002 | |||
e41d7451bf | |||
3bb27deecc | |||
a34d4c6a5f | |||
02d9e18279 | |||
8536df16d9 | |||
a94f051d43 | |||
78200868f4 | |||
256b2dc9b7 | |||
51285666d8 | |||
557d309377 | |||
4ba4427510 | |||
9f576b0233 | |||
343dcfa0c0 | |||
457eb5d69c | |||
5863f50316 | |||
8ed22ce6e7 | |||
10437ab85c | |||
38a5a2a955 | |||
56e88058f1 | |||
5f6b7ffadb | |||
6d2345accb | |||
cc2a2e4bbc | |||
e72305e685 | |||
c2279dd651 | |||
9cc1cd29ed | |||
894af28529 | |||
bf4b75ee9c | |||
21f60c5dce | |||
aeef19e2a6 | |||
64ffd9e99c | |||
944ccc9a94 | |||
9262727ae1 | |||
3963e0ab39 | |||
d2a3b76a71 | |||
377e3ac962 | |||
a191c89193 | |||
5725f71777 | |||
88a5b8da0f | |||
92da5430e7 | |||
50eee42668 | |||
d07b9181fe | |||
5f0a0fd8d2 | |||
a205807191 | |||
b039ec3da3 | |||
5ee9630be1 | |||
c4044102d6 | |||
215c4aa8fb | |||
3871f89119 | |||
d853d19dd7 | |||
5d403966d5 | |||
20acb86ca2 | |||
e8e430e630 | |||
85ec34d1d9 | |||
a9ce454b21 | |||
b6146394ae | |||
764726d78e | |||
65344fcac9 | |||
ebb3236ad5 | |||
9d44ae3d32 | |||
d2d0715568 | |||
b986c54079 | |||
8336881a85 | |||
b0d27ee896 | |||
9ba2ab5cea | |||
1c9be9b5aa | |||
49997ca932 | |||
195c5c21d4 | |||
a0b240884b | |||
9afcb00216 | |||
bde6416b40 | |||
06487237e5 | |||
964884e761 | |||
09e0b0ac6f | |||
984e7b8e17 | |||
abb3bd266b | |||
298f1fb6a6 | |||
cef51a7e0d | |||
ddb62feae6 | |||
fde026a9e4 | |||
78a8ba2307 | |||
ffc63fc6d6 | |||
9757c137db | |||
0b59e664ec | |||
104302a958 | |||
ba2c077b01 | |||
94733069a4 | |||
cba388bb65 | |||
efbd8fca4c | |||
6b38d249c5 | |||
abb0e279a5 | |||
d191ec4c33 | |||
873b6da120 | |||
f0e7f13f30 | |||
ccfadb9b47 | |||
8dc509f688 | |||
72e0b8f7dc | |||
b5c4d8ba79 | |||
5c95b376b5 | |||
f2d3bdb801 | |||
3b9411cbeb | |||
16eb8ced58 | |||
63a94ee941 | |||
ddcfd465ad | |||
3b4965279c | |||
914ca88e5d | |||
675cb87c76 | |||
53df0d8b3f | |||
8a7a783ff2 | |||
e68c978202 | |||
272ad61ab1 | |||
5f7d4faa88 | |||
235dec26fc | |||
52da220016 | |||
cbfc9cb344 | |||
ed81cb94b0 | |||
1f54e64fcf | |||
20cf61756a | |||
ea6673947c | |||
5e6317fecc | |||
823fa4689e | |||
6c9e712c34 | |||
4845583dcf | |||
8b655c7be3 | |||
a06f48e357 | |||
841206c678 | |||
296851797b | |||
ee5df00146 | |||
89a0f2457d | |||
26d2ea8afc | |||
2686316c90 | |||
83b69e8edc | |||
35a83b495a | |||
dfe0130753 | |||
2fcb4cb769 | |||
da4bcd5d91 | |||
512340e39b | |||
284dc67076 | |||
38945955ab | |||
b7837389d7 | |||
5c328adb4b | |||
60af19f0e1 | |||
1d65b38b28 | |||
64ebf278c0 | |||
416fd085b1 | |||
839edaa15b | |||
ac311911c0 | |||
aa480fee72 | |||
b6ee20846b | |||
b5fb05b735 | |||
7b3cca20d2 | |||
8f5360c387 | |||
ed25a29cc8 | |||
a7a1851c0f | |||
f2f4b905e5 | |||
08dbe87819 | |||
218b037d98 | |||
2150a8f9d1 | |||
720a3c8edd | |||
585ea5d600 | |||
5b5d31fa9a | |||
02b7e61ef7 | |||
280d8f3148 | |||
5a405011de | |||
2374e16104 | |||
561066f899 | |||
903a0f0513 | |||
f83289b1a0 | |||
77d3668432 | |||
dfa5103b1d | |||
1f692ae263 | |||
1562bc91ba | |||
6bd95c1455 | |||
4f5e405676 | |||
86eb46af09 | |||
6651aab11f | |||
5a8b1bcaec | |||
ac6227e434 | |||
96e34c1d36 | |||
4038150246 | |||
24eabb9bb1 | |||
574bbea747 | |||
a1c53eec6b | |||
84463cf0bd | |||
2dcf714d2b | |||
06da60f4b7 | |||
ff1b110ae1 | |||
748c2d6c97 | |||
339071cb07 | |||
a37de36fa6 | |||
cf6ffd5469 | |||
d40ff0bb89 | |||
c4296285f3 | |||
106a28b8dc | |||
bd498977bd | |||
7c52bc9768 | |||
643c71740e | |||
40a3cd2ab1 | |||
3db0ae1dac | |||
07b9be798c | |||
b4a062983b | |||
f11f4e0b45 | |||
450d3630cc | |||
166688348a | |||
39ad50657e | |||
a62a6ba40a | |||
0bb78b7ef4 | |||
861be30021 | |||
27aa9187f0 | |||
c58b0ff787 | |||
392de4af67 | |||
c20a5d65d8 | |||
4422819754 | |||
6eaa09ac20 | |||
c0adae69ac | |||
cfedc77ce1 | |||
96eefdfebc | |||
7bf7ec6d9c | |||
60f38eab78 | |||
209aefee57 | |||
be79942ebd | |||
1beaf81e98 | |||
12e4c738c9 | |||
8822460858 | |||
53788ef827 | |||
b8e82e4db3 | |||
94690ec5b0 | |||
77875a270d | |||
6d272cc5f9 | |||
6d280ea31f | |||
7354206c74 | |||
39b0286d6b | |||
dad9338c82 | |||
f6da89f74f | |||
3437d56904 | |||
3c58878b19 | |||
c5cb7009ca | |||
adc27398fd | |||
dd06a871b7 | |||
014b6cb397 | |||
d44827a4c5 | |||
3cda7128d0 | |||
70bc485755 | |||
0a40024995 | |||
985627bd65 | |||
a9ec6b9064 | |||
f5864afdbb | |||
21e2f3c1db | |||
7b94bbf3e4 | |||
71ea19902a | |||
9e7363f686 | |||
2768158eaf | |||
f8fa47e939 | |||
d4ded1a60d | |||
4e76cac5b7 | |||
0dbdd5cd3c | |||
c08403935f | |||
4893002408 | |||
29095766e6 | |||
eee2146735 | |||
5c21af95c7 | |||
cb6fc9c7cb | |||
68a581a04c | |||
7b834e02ec | |||
8e960d4052 | |||
34acef58e7 | |||
c1ee943533 | |||
f7d7789915 | |||
9c19eb906b | |||
73b7d99dc4 | |||
192cf9ddf5 | |||
d894aa9101 | |||
aae5a4cece | |||
16e3d7e96e | |||
e40ff36832 | |||
c5f8c9586f | |||
4cfe92c47a | |||
9d2776183a | |||
2faa89852b | |||
d84993faf1 | |||
a9efc48e71 | |||
8543c347a8 | |||
1d4502944c | |||
448ca384cc | |||
d426af741c | |||
6ab2a871ce | |||
2c9951273a | |||
6fac901151 | |||
74b311a472 | |||
81d298dc6b | |||
35597a8349 | |||
45cbc430e8 | |||
03fc7fe8c2 | |||
8a91d71625 | |||
4dc6d748a9 | |||
99587a9907 | |||
eebd736cfe | |||
bb50cda181 | |||
05c4fb2fac | |||
448264be39 | |||
78fdf9a11f | |||
71bb4b3ee5 | |||
a2bb81c406 | |||
30bd2a3fc7 | |||
7cbaf1076f | |||
a8b57256c8 | |||
329a6e00dc | |||
c83a3f3372 | |||
0906ee8a4e | |||
23cd385f20 | |||
45b10a1f0f | |||
469afda53e | |||
c2bbda02a1 | |||
e744409cb9 | |||
0a8b3816f7 | |||
bc21aa0124 | |||
2577f5eebf | |||
f575ba60fb | |||
5b5de6662f | |||
a8b75c3d41 | |||
19d8b221b4 | |||
345fa521dd | |||
16493e9769 | |||
dbbb7385f5 | |||
3a7c9e4c62 | |||
408618b836 | |||
771c0170d9 | |||
21b988f554 | |||
1438922ffb | |||
f1e4292072 | |||
fd46b49ea6 | |||
46502e4d61 | |||
b44b06c2c9 | |||
5ec67ee2a7 | |||
61b7703406 | |||
49dc819d23 | |||
03c8e7428f | |||
70792c744d | |||
fdf226ab69 | |||
bd65b63c65 | |||
09226cdd75 | |||
3c2b165de1 | |||
76f63bc6a5 | |||
3531bb7118 | |||
55e8dca8de | |||
820c4b9b16 | |||
fdffcaba9b | |||
a9e7c90960 | |||
5a86f85936 | |||
8ecb632d70 | |||
8ad4ad57d1 | |||
854b5b7da8 | |||
231962aaf7 | |||
51e6f33d32 | |||
476988876c | |||
c3d61bc63c | |||
6f4a39c337 | |||
a472eacc07 | |||
078475a082 | |||
de18da2a0d | |||
6d760666a9 | |||
b2a0be87e8 | |||
4c8ea12903 | |||
e94270946a | |||
40d21b808d | |||
095def3845 | |||
153660fe7b | |||
3dd05ef7db | |||
cfc18b5a6f | |||
57496926ca | |||
19e4ee81b9 | |||
f74d97e1f1 | |||
011fab37af | |||
8296dcec09 | |||
3df8363a94 | |||
5cc84ed4bb | |||
2f08ed8d3e | |||
1386977a34 | |||
cdb6c9cda2 | |||
13b1d85058 | |||
c6409cb624 | |||
573c047d50 | |||
f42382db3b | |||
762a94f2cd | |||
5b597de18c | |||
4d28167bc0 | |||
61c73576c8 | |||
116b64de25 | |||
b785503543 | |||
fc8f4688c7 | |||
3f6dd6cd42 | |||
2147ce45c2 | |||
c701664e07 | |||
a4915ad634 | |||
fe3a55966d | |||
f1e8176995 | |||
621604dc66 | |||
5654f2f4e2 | |||
b1231593b6 | |||
b1df54501a | |||
d449ea5ca4 | |||
583c5ffcb5 | |||
7879761a41 | |||
f7dfd2325d | |||
b03560b670 | |||
f25e43fab8 | |||
903ff9047f | |||
66ec4d1f5c | |||
93055f78ea | |||
33500e986b | |||
b26f99787e | |||
9d66b5b09e | |||
8bdca5c03e | |||
ccb41632c7 | |||
cd532b00d4 | |||
81abc39929 | |||
b4cde697b5 | |||
7531b48d02 | |||
2bc1217409 | |||
b73ba68215 | |||
e1e7912ab2 | |||
e988f59c08 | |||
3810e4bed3 | |||
27233cff31 | |||
34993f7691 | |||
7dc1dff816 | |||
5677bf73ca | |||
97ef1c27df | |||
04baa46efe | |||
4bdc91892a | |||
b3a763a718 | |||
7ee6963f5d | |||
1eab4f5f07 | |||
cf103de4a7 | |||
a9a552c112 | |||
f5f85bb528 | |||
bdfef4ed16 | |||
4d80ce5b4a | |||
09bb114a4d | |||
3bf8c18c56 | |||
45994a53ce | |||
875db11822 | |||
2e4a2a0e5a | |||
9fa7d38133 | |||
927454c8fa | |||
0e28297e68 | |||
1eebceab27 | |||
9a70f84e60 | |||
8b3efdf229 | |||
1427d73b66 | |||
62589293aa | |||
749d043258 | |||
a6210466c7 | |||
71c65b47f9 | |||
0ed5dd0d7b | |||
b716046b97 | |||
3143d188ae | |||
b54e7214f0 | |||
d6dadc6efc | |||
546a8f9218 | |||
fe49a7fc40 | |||
447926dc08 | |||
258da88765 | |||
e79dd6aa2d | |||
2c37cc5fcb | |||
206c9bdd74 | |||
4264bd3bd2 | |||
ae5cd51f50 | |||
df877a7d5b | |||
4572157c49 | |||
6596c72131 | |||
f1f578436b | |||
f827e1532e | |||
bab271fcb4 | |||
0792f1a7a1 | |||
ac50ffca5e | |||
caa252e57b | |||
d5edc748d2 | |||
718fa35167 | |||
c177d889a2 | |||
3f7ebde037 | |||
4656c6f5cf | |||
e0fb50cc3c | |||
7d9f5d7538 | |||
2ac8ebd3ef | |||
871267d578 | |||
d050ce20a9 | |||
6c933a4485 | |||
c33e3be735 | |||
3ab8a0c438 | |||
12a427e158 | |||
0052c6b120 | |||
65a767d9b0 | |||
0e945e465d | |||
72257ec87d | |||
e5405e4ba2 | |||
b60d714acf | |||
edf5053bf0 | |||
3869818d8f | |||
abc1580fa9 | |||
286a249a9a | |||
d828664d0c | |||
355ced92eb | |||
9e1158de4f | |||
569766fa8b | |||
e987ac4034 | |||
c2efa23e94 | |||
d2774421e8 | |||
8681f79182 | |||
b10d7a2e51 | |||
a56d33d7ca | |||
2b45bd2a63 | |||
7986e7ce7e | |||
311b47720b | |||
582551bea9 | |||
19a0349681 | |||
883b506445 | |||
d381c5fc8a | |||
ad506a7aaa | |||
ef3cc8e6eb | |||
561b78a5b3 | |||
a7ea2e5566 | |||
1c94c32f4d | |||
d5ace7a562 | |||
2ed251a5db | |||
8d0d05c65f | |||
ac24a301bd | |||
ba440a04d1 | |||
cd65fc2a5e | |||
e69af1a3cd | |||
d1393b0581 | |||
46bb4e37ba | |||
3a8e1661fa | |||
0598226e24 | |||
0b50258814 | |||
600d53c68e | |||
f0e962c55e | |||
73a939e76c | |||
dcdd73065a | |||
34eaf65a79 | |||
27e710019c | |||
c2c361efcf | |||
f020a5cdea | |||
530e742628 | |||
a90063a827 | |||
de05d1bf46 | |||
1fb948461e | |||
27c050be86 | |||
f66ce096d8 | |||
2335075506 | |||
b3fc357a15 | |||
b3d98cba77 | |||
146dbf1270 | |||
55bf0e554f | |||
fa7da0ca5d | |||
d49459750a | |||
d6b56c2380 | |||
7fc1ee67d1 | |||
3e6503789f | |||
8bab6dd239 | |||
a9008eecba | |||
73b3ed18c1 | |||
a93ec73e72 | |||
9a290f0c22 | |||
5de916ecd0 | |||
44891996b5 | |||
bc585f2724 | |||
d629ed7d5b | |||
8c66a25270 | |||
82e8e8c638 | |||
27ad984626 | |||
1f04f70eda | |||
eea989bef8 | |||
d6cef88dd8 | |||
da4de21f28 | |||
eb0fd7967c | |||
56245c6aa2 | |||
ab3f2365fd | |||
66d0e4e656 | |||
0f7dd62f16 | |||
c4e10ea9ac | |||
f1cf5298d8 | |||
22d3943831 | |||
b688dee4c8 | |||
71e0f89594 | |||
5a21dc5340 | |||
d037c082fb | |||
9c768501c4 | |||
b50edfd1f3 | |||
dd7910347a | |||
65769699b0 | |||
8b685466f5 | |||
91d9e8d649 | |||
746efe7eba | |||
ecb2bd0cbe | |||
7e8a2b9cec | |||
1f7296c093 | |||
d1f7900eeb | |||
393f703a97 | |||
7a7b3a6cb9 | |||
0949a4b045 | |||
7a4a3c850f | |||
8a5cf8f6bd | |||
aeb17d8d44 | |||
85d6ae38b9 | |||
d3dda614dd | |||
1c8a58963c | |||
19a9dc67bd | |||
a0e0f3123b | |||
b15474c6f2 | |||
356c927d12 | |||
b89c0672e6 | |||
0a2f6ddc64 | |||
3ea655918e | |||
3875f02a52 | |||
06aaa0c50e | |||
8c993dca03 | |||
2c11205b96 | |||
d800d2f5d7 | |||
24646e7eb8 | |||
6149ce28a7 | |||
c8bdacb195 | |||
5c9e53a25e | |||
ba9fecd068 | |||
2c3c235969 | |||
7b2f757b2b | |||
a67f2314f9 | |||
0a51ccbd68 | |||
2932377769 | |||
e5de1f771a | |||
b48f000657 | |||
d8929c1d73 | |||
92ffc465d6 | |||
c1a494bc37 | |||
783654e6a3 | |||
dee0e008f5 | |||
d7b9345b6d | |||
f158fbd131 | |||
1c4d233fe7 | |||
d575915d7a | |||
ed9d9d5096 | |||
2d2ae9b8d8 | |||
fa7a3e3449 | |||
a057789235 | |||
20a8f0dbe5 | |||
ad7aca631d | |||
c899b0a74c | |||
5ba5da5d25 | |||
4d1ed509e3 | |||
85b8a15374 | |||
920982c4e8 | |||
254e58c28f | |||
f9d72bd85b | |||
b8ef20e353 | |||
c8ef5b5811 | |||
22f4cd26ae | |||
aadaa20706 | |||
f2ef90b240 | |||
f80f97253c | |||
ee8da36d08 | |||
9d4111d69d | |||
180e617866 | |||
bc798b182d | |||
941362014b | |||
dfd30910aa | |||
be7ac9fd41 | |||
35e882e74f | |||
58b38c9201 | |||
ed3af5f751 | |||
5beaf6d735 | |||
b0e2ebda70 | |||
cd21df3572 | |||
c7e48350d3 | |||
6af41a4543 | |||
f9eb8a44d1 | |||
d43394f7b7 | |||
dcc4bc2735 | |||
5dee8e26cc | |||
df6acedd25 | |||
ffd1ac425e | |||
edd01615c3 | |||
9700e80698 | |||
cd735c4837 | |||
f93aae4802 | |||
b2c66949b2 | |||
eb3586d777 | |||
5b5ffe75d0 | |||
e68e69e7e5 | |||
70ffd267f8 | |||
b68e561c0f | |||
93596dff3f | |||
ea27704ea9 | |||
35589a6b3c | |||
598a75ec1c | |||
cdfb635737 | |||
37fceda7e8 | |||
2d499de2bd | |||
91c75f99fe | |||
6e38515402 | |||
d599fd3434 | |||
b2e6ad85ea | |||
d8e2795368 | |||
7a41b19e58 | |||
bc9e482b39 | |||
cab1d0ef0f | |||
0c282e826a | |||
8112b0baa7 | |||
c34cb01404 | |||
f1541e65b3 | |||
9bad70be5e | |||
5bfcca2d5b | |||
73d152506b | |||
3154cea0bf | |||
ac3e624d0f | |||
f065a2ecb7 | |||
a46df6f829 | |||
98fcf8c6ef | |||
da60381c89 | |||
7d32879929 | |||
d64cc8d87d | |||
cebf69933c | |||
c3ae34f066 | |||
4f581671dc | |||
5a542d8ba8 | |||
dd23bab3ad | |||
14d28d7473 | |||
992293a196 | |||
9908def857 | |||
552d1ed61b | |||
5782f063f1 | |||
4ae7df27d2 | |||
c3c2ad1454 | |||
c51aef9f7d | |||
bbfb4e1dcc | |||
e0fbd4b624 | |||
8e3bf3907a | |||
21dcfc89e9 | |||
9613772455 | |||
f3d741854a | |||
c4ecbf0a7f | |||
13466604f9 | |||
cdc7b03e67 | |||
e372cc779d | |||
6834c4992d | |||
1cf807c319 | |||
833048f310 | |||
38e5c3f918 | |||
b4e82b8bc7 | |||
db3d5d4941 | |||
a6e7123995 | |||
1c2abbc61d | |||
7648bb8ee3 | |||
35197acc1a | |||
6622826587 | |||
4fe919335c | |||
a858f6ac42 | |||
cee26826d7 | |||
e89fe0a9ff | |||
d10bbf0cbc | |||
05d02fa90b | |||
796fc66771 | |||
6cafaba993 | |||
60b97b27fa | |||
355ab5b3a6 | |||
ca09701343 | |||
4f27611ae6 | |||
24e647e0f7 | |||
9cd510abaa | |||
d07ed581a9 | |||
5ab92ce844 | |||
76797dfbd4 | |||
591f742d42 | |||
20953ed492 | |||
f3b49378e4 | |||
47b6b05017 | |||
f9745327e6 | |||
659adf83dc | |||
a187c782aa | |||
950f2a38cd | |||
f2371487a1 | |||
6ca81fb98c | |||
af35ab56a3 | |||
15dab7c5b2 | |||
37f8fd6551 | |||
f04b606bb0 | |||
2ccc65d7fd | |||
7501ad11ca | |||
331a051e75 | |||
92f1af86d8 | |||
a6cb86bab3 | |||
76beaa2097 | |||
94272af45b | |||
ad95601e3c | |||
17c6d6a92d | |||
633cf63682 | |||
92d565848b | |||
617d693102 | |||
4530b93a6e | |||
dc45559c17 | |||
efab03274f | |||
f2e2ce15cd | |||
ddd5a235c3 | |||
2cc2196140 | |||
65cbcb2f73 | |||
002101521c | |||
902759e1c7 | |||
c564475251 | |||
4cd29f791f | |||
3eff7be9a6 | |||
e7e82cbee6 | |||
73bce40287 | |||
5fc23caef7 | |||
1a4d23742b | |||
8923103c3b | |||
f34f8df319 | |||
21568106b1 | |||
363b9ba415 | |||
0409b4ca49 | |||
7611f92f5b | |||
7aa9751054 | |||
77b52d65c7 | |||
5df56b47d6 | |||
1eefde7f0d | |||
44b31f326b | |||
b5431e4cc0 | |||
83e99fc72d | |||
cd6175827f | |||
e77710a372 | |||
b6e95bb96e | |||
cd95e078fe | |||
548f3dd5cc | |||
d5195d4097 | |||
fda8b1d87c | |||
7140c9cc34 | |||
4d338a4f5c | |||
6a6b43de07 | |||
309ef0f354 | |||
81fc657356 | |||
29c72abcc4 | |||
79f3f3b456 | |||
d1b35f9174 | |||
f3dd9b5b31 | |||
72736a1b09 | |||
a418397174 | |||
ab8eb4f652 | |||
8d081ea7af | |||
d5c528ac2b | |||
7141c15e65 | |||
5357b1548a | |||
928ec1c5f8 | |||
dc8dac7c35 | |||
c593dfc26c | |||
6b31d82263 | |||
4ee3fdaf7f | |||
41ae8e76f0 | |||
8a52375fb8 | |||
87b3b718e3 | |||
46eeee6b5e | |||
529805508a | |||
851797aecb | |||
13d0815839 | |||
dc6e7eb19b | |||
e5419febe4 | |||
69b75b7fd8 | |||
4eb8c9b2dd | |||
827841ec5b | |||
0a50a3f564 | |||
ce9271739f | |||
2621d6be8b | |||
d76a2ee9c9 | |||
b2596dd302 | |||
4d0973d7ba | |||
1afdb3cbb1 | |||
53694eb64a | |||
31a3a19dac | |||
a50b8a2694 | |||
9e8108ee14 | |||
d53c898499 | |||
552985e305 | |||
20e874d3c6 | |||
471a1b6d12 | |||
f999d5a156 | |||
f74d7727ca | |||
3baf815d76 | |||
96cadcc29e | |||
31b6687894 | |||
381d4cb30a | |||
5030ffb01c | |||
9ce0870f6c | |||
246151b2f9 | |||
83f1856d6a | |||
c280fe816c | |||
b071b66b45 | |||
fde65c7e88 | |||
bae6b91e7d | |||
2b714df64e | |||
c7572ac1f9 | |||
ec3a78289f | |||
f303f0c17a | |||
f543834be9 | |||
6bef1c4169 | |||
652ed0cf6d | |||
559f54e92b | |||
17e1d7f117 | |||
5fa54a92bc | |||
ba7956f521 | |||
c2fa4b7191 | |||
d32f58926d | |||
6a0fe93ba9 | |||
1fae8d6377 | |||
dc060e8b64 | |||
b746e0c9f0 | |||
e67b7e87b2 | |||
0a0b84a07d | |||
927b4d01a9 | |||
5035a42287 | |||
40150379ae | |||
1d24e2cf23 | |||
9e36539052 | |||
0602f68ae3 | |||
2b60d1bae1 | |||
0b43e3cf32 | |||
c9d636aa11 | |||
be88cc7697 | |||
ba07f39347 | |||
4c8e11a577 | |||
3d6c44e2a7 | |||
c60091b949 | |||
a504fa835e | |||
8811337622 | |||
921fb9f2ce | |||
e93b5a1d5b | |||
db7a1f19ba | |||
ef27919f7f | |||
c571b2693e | |||
8bcfb2d465 | |||
93f464a145 | |||
95b7896d9b | |||
ad26bed0ed | |||
62a95823e0 | |||
c8d83dba7d | |||
22f5925202 | |||
cd52d8a3be | |||
a9d6fd9afa | |||
5c53cf6486 | |||
74882c6c38 | |||
c8947d77bf | |||
4f3acdb004 | |||
6404dd8293 | |||
f19970a481 | |||
6f0631c978 | |||
e5d06e479a | |||
d523613329 | |||
cfcae6b293 | |||
000a8e25a2 | |||
8a3b0b366f | |||
383f0a1f30 | |||
e323c07ab9 | |||
f9908cd436 | |||
24bc4b66d0 | |||
2351896cc0 | |||
307011a96c | |||
6f3368ef16 | |||
cdf791f0c5 | |||
75578f41e7 | |||
ffb219fb91 | |||
eb2784eb81 | |||
28ee0612cb | |||
a80921b45d | |||
0db88f34b8 | |||
35f0ee510a | |||
d7df853bde | |||
0387221da8 | |||
c39c8ebcd0 | |||
5d2af54730 | |||
a9be2ebf1b | |||
ec2d8cc2c8 | |||
05d66bba3f | |||
b14417498a | |||
05becf8431 | |||
160c38b5ca | |||
9b0fa0dedc | |||
533c64d4ea | |||
c1157d62a8 | |||
7b1e9286d8 | |||
01fb8e6635 | |||
9d90128463 | |||
34cfc9f474 | |||
ebe1e73b1a | |||
8ce0a67c81 | |||
c065fb1422 | |||
cda35101df | |||
c32dbad747 | |||
30b6542fc8 | |||
2b6a653050 | |||
34d75e8918 | |||
2c25055828 | |||
96f629d441 | |||
a2770c8a52 | |||
4a3fd5e855 | |||
608017776e | |||
0c7f05f56a | |||
c6335c128e | |||
fb42d5908e | |||
8609543ad0 | |||
f83f1ee0ce | |||
1db6870a81 | |||
c19c69f336 | |||
b390f441a1 | |||
79f564be46 | |||
23d59df81a | |||
ef3e12e803 | |||
5fe88d63ef | |||
0f3a8f369a | |||
26d5d17ebe | |||
bb7ffce7eb | |||
551586ced0 | |||
1dc8ba6920 | |||
d773b6a00a | |||
f6cd26b0a6 | |||
5a52c0b71d | |||
662da0d728 | |||
df59e969cf | |||
d27e5512c0 | |||
d48fae3566 | |||
3525c9c074 | |||
05774f6c8a | |||
16447ce75c | |||
15f1eb28a2 | |||
e50f537667 | |||
ed8364741b | |||
6c1cb089b5 | |||
4b98ed114e | |||
d308e55e12 | |||
10bc7e948c | |||
9988471fb8 | |||
05fa9bc9fb | |||
57b88ec2d6 | |||
b1c9bf14b2 | |||
588fbfd848 | |||
b2a24e021f | |||
661a04798e | |||
665ccafd73 | |||
f35dbb99b5 | |||
a393f84fa4 | |||
92c2c33a84 | |||
9802debf71 | |||
2287938f5a | |||
5103f080e9 | |||
1f20ef9787 | |||
1ad6558229 | |||
d61a0dfa22 | |||
ac510b67cc | |||
5ed091e260 | |||
cc2c8f6b00 | |||
c28952c707 | |||
30c3e5a84e | |||
2ff3873881 | |||
aec51d616b | |||
c5996529c3 | |||
00c3693daa | |||
fed86fc8ac | |||
705ee46f31 | |||
f210c41c1f | |||
863eb3c559 | |||
b6b52e62b2 | |||
dd9b08cce8 | |||
e61d82b9be | |||
aabc898f3b | |||
bdeac30a96 | |||
4afd2b4138 | |||
dee4ecbb3f | |||
4210b0e66a | |||
2d6c44b54a | |||
c45283216f | |||
826af401a9 | |||
28c2b8f432 | |||
c9ab8e4be8 | |||
1054f6a9ab | |||
f6eeb9aa66 | |||
91ccc9af98 | |||
ec90fcd290 | |||
a664f5a6de | |||
7643d979c7 | |||
d18463dcdc | |||
f9f917bfa4 | |||
e89bf01c2b | |||
d04a515eb0 | |||
0ae89ac096 | |||
6ec5d5daaf | |||
62b1a08f06 | |||
c54f5e0ba2 | |||
37a8f1037e | |||
4ba81bf3eb | |||
c204835969 | |||
ac28ac324d | |||
8aa3fcfb63 | |||
1beadb8607 | |||
bd8724e652 | |||
73d15edef5 | |||
adaa157317 | |||
791caf0037 | |||
8ab773538b | |||
5c88f662cd | |||
cd7aef2139 | |||
50c6efa187 | |||
302c5d5005 | |||
b6b9ede425 | |||
118f0520a2 | |||
ba80bd43ad | |||
ed6298c33f | |||
db5486a347 | |||
b9b58f7ed9 | |||
33f5aafd6c | |||
4b34ef9036 | |||
8c409e9251 | |||
398e70ad39 | |||
9407c12923 | |||
a4693ef679 | |||
9d5f760597 | |||
8bebcfb844 | |||
8a0eb08745 | |||
1dce4699de | |||
7bff919782 | |||
c6dc78183d | |||
9a72f19b97 | |||
25a952755e | |||
b2da2978ee | |||
8b9400ad92 | |||
3571450b42 | |||
72bb5bdd5a | |||
04a9eb8820 | |||
0f002a5b18 | |||
b066b8d15a | |||
7b511462af | |||
3a53f67911 | |||
155b1e2b35 | |||
aad5795408 | |||
83b97c485b | |||
5db89071d4 | |||
c29ab86d85 | |||
6b02cb9b44 | |||
88c607da03 | |||
f9fd4926ef | |||
986038242a | |||
8e84f8a1c4 | |||
edfbc25768 | |||
05a1c6c183 | |||
534cbb4bf5 | |||
d9ceb42bfe | |||
8d6943227d | |||
3011cd86bd | |||
cfba38b462 | |||
11e4385173 | |||
ad29b12cde | |||
842459aa46 | |||
b033416a45 | |||
7310b09a1a | |||
bb2eda2d15 | |||
0114cd97b6 | |||
fc13cdab3a | |||
3644036693 | |||
c397297eef | |||
1eea2b254e | |||
0e04467b8a | |||
9fbb3adbe2 | |||
b6f29b4448 | |||
421d8916a6 | |||
a38a0d6f87 | |||
7a4a635399 | |||
557d54b3de | |||
c47902a471 | |||
25cd6e4321 | |||
6ad5fa0d9d | |||
666336be1a | |||
77bf90dff1 | |||
8ad0205948 | |||
7ff17db113 | |||
1d11fdecdc | |||
b1ef30aa20 | |||
28659efa69 | |||
6c59894a29 | |||
47b8b48ee7 | |||
3969009fe7 | |||
ecb068019b | |||
5114411749 | |||
08f21dbf51 | |||
d8c7c274e4 | |||
fadabf79e3 | |||
23cec1e8e2 | |||
c36ea0221e | |||
685a6507b6 | |||
846354473d | |||
390cfb793b | |||
17392f663f | |||
8b6fa1cf19 | |||
909233f724 | |||
0e82970a29 | |||
cdbb2473bb | |||
c20060d259 | |||
38926f7123 | |||
0efd89ae5d | |||
718d2ae2ee | |||
af9dcad8e3 | |||
772b529a8e | |||
111fa60a93 | |||
e9f236b70f | |||
78d3f62b6a | |||
d310a9c0b4 | |||
7dc524ed58 | |||
032f8b7840 | |||
4f2b9a4c28 | |||
1ac7bb3bb8 | |||
f302f70330 | |||
555dd93ed9 | |||
96b0a1c75e | |||
c8a0ed40bd | |||
e11c20541a | |||
c75e216871 | |||
fd1d60f03b | |||
e8a6c95e2a | |||
358a6750ed | |||
4320859e1b | |||
98e7a38e50 | |||
229e770a1d | |||
5036086fb3 | |||
97d24563f4 | |||
655ed851f0 | |||
f8f79dc76c | |||
a574154108 | |||
7f976381d5 | |||
ac80df0959 | |||
aff85b5037 | |||
5691063ba0 | |||
7498758584 | |||
92d6aa1f32 | |||
3256ff1c73 | |||
f9c1de46b3 | |||
a9ce0f7afb | |||
1a4ab2c57a | |||
77d1fc149a | |||
c2a42d5d2b | |||
01d5c29513 | |||
7844e3a275 | |||
a5638a940c | |||
4665726f48 | |||
e8ad0d1776 | |||
28022f472d | |||
4f3433b5bd | |||
2185e7cee9 | |||
c82cc47767 | |||
66f5e30d7c | |||
1a0da11e55 | |||
abc8878547 | |||
75e9d3f634 | |||
248caefb7e | |||
853d1de6ec | |||
84dc6ae76b | |||
05219a54cd | |||
4b62a722f0 | |||
61b69c63ed | |||
09b39bf77a | |||
42f6baeaec | |||
8ef183b593 | |||
2cb066215a | |||
200e190f70 | |||
44f829dbc6 | |||
401c9efad7 | |||
900bf8e483 | |||
271ced8ac4 | |||
c5bd3f0773 | |||
ad23921814 | |||
31cbec0857 | |||
d717529e9a | |||
c68fa27444 | |||
d2507ac760 | |||
4ce0d5e024 | |||
5d9e573b3e | |||
51839ca677 | |||
7225416661 | |||
5e4fa5cf07 | |||
fa28b28d0a | |||
f0ef72d6cc | |||
a58c9f83bd | |||
624a33f7f8 | |||
20a033e4c9 | |||
93c331d103 | |||
51c477925a | |||
1daa8aa3a1 | |||
62bf777ef1 | |||
aaf3edd131 | |||
6bba289a3c | |||
b0c735f72c | |||
0e2047f9ca | |||
e30ad2ec2c | |||
c509057f65 | |||
e138add584 | |||
49777648b3 | |||
cb87fa0970 | |||
649e276610 | |||
5ef11774c2 | |||
75db2c5241 | |||
c8ebd11d63 | |||
6651aa1e1d | |||
3f28d08778 | |||
ab28676d02 | |||
577a80371f | |||
01fdb4afc6 | |||
be7504d451 | |||
169e4e862d | |||
abc3de7efe | |||
0b1bb172c9 | |||
0856516ae9 | |||
705d3aacff | |||
1d0078415f | |||
9d1df21d91 | |||
3f36a3c119 | |||
8c15ccecd1 | |||
e966869744 | |||
6b017fb388 | |||
bb6f59e423 | |||
c9cec60007 | |||
f356d03362 | |||
0520ca68b4 | |||
8e1d53b5e9 | |||
f5b56c627b | |||
740d85cad8 | |||
33bba094d2 | |||
f88c4b77ca | |||
75da6e4c4a | |||
1864f60afb | |||
457c15cd6c | |||
28feac9411 | |||
9153331303 | |||
9d5c33f9dd | |||
2713b7877b | |||
2f0fef8ee1 | |||
259f872cea | |||
68ed8f1b6b | |||
ef7014fe19 | |||
46ad3552c7 | |||
4965226f3f | |||
ea546f5069 | |||
cf32213079 | |||
ce6a2ba836 | |||
0f4a089c32 | |||
3c77855b39 | |||
c1579222bd | |||
f863ea0db5 | |||
e4342743c0 | |||
9e8d31d532 | |||
f75a50c1dd | |||
c671706518 | |||
ead21c91a4 | |||
87dcd5eb6f | |||
8faf6364dc | |||
b71fe311fc | |||
bb50fc131b | |||
3aac2fefd7 | |||
fb67e37339 | |||
648c514c28 | |||
511e832ee2 | |||
09f8d8f7ba | |||
1205f54d01 | |||
b5032fd374 | |||
a7a9463624 | |||
59824e40e8 | |||
a51a5c2968 | |||
e3c11045bf | |||
414e58edb5 | |||
3bb3bff1f2 | |||
d2d4e7d783 | |||
ee1b574baf | |||
c0f3778dda | |||
d4925b61ff | |||
5b104936ae | |||
14988d4415 | |||
cd953ceb48 | |||
726fecbfb6 | |||
818bb9b697 | |||
e4586249fa | |||
4c1e978536 | |||
3d62546314 | |||
b9eab463f7 | |||
dff4795e49 | |||
ab74e1ed4e | |||
902984cc10 | |||
ce431f279e | |||
7fb2f2069c | |||
c269bd5d3c | |||
b72eb0783b | |||
a801da6f7c | |||
6fcd3709cf | |||
1b2754dacd | |||
e617ca6323 | |||
15376a6d24 | |||
867705bd2c | |||
6ab19dd095 | |||
99fdb9ac41 | |||
2827ca1559 | |||
8ea03d0380 | |||
d1ec2e18cd | |||
aa58e4bba5 | |||
0a97f0b645 | |||
8a5cf896d0 | |||
22c79df98d | |||
a52798543a | |||
1cbdb9cd17 | |||
4c9b8ebb0c | |||
6aea629cd3 | |||
649fd5a7a9 | |||
40c4eb7240 | |||
cd05ed8de9 | |||
42e7fc5252 | |||
7740fc071c | |||
bdf6af9bd6 | |||
2f83efaac8 | |||
32c5ab956c | |||
b111ca9471 | |||
725f909ff8 | |||
427f0d021c | |||
9fc9d53566 | |||
6dece68bb8 | |||
0676fef61f | |||
1d52cfba13 | |||
bfa381b35a | |||
f78406392b | |||
623edcd2d8 | |||
b5e350b18c | |||
87cf434929 | |||
4bab25b366 | |||
6896305e34 | |||
8ccafb0524 | |||
8a92a1f13e | |||
d0059b5d75 | |||
fa8e059f28 | |||
8e18d6c6cf | |||
afe0e45453 | |||
2e3e41ba64 | |||
d74dd1126b | |||
0ff99081bd | |||
6f4b6783c0 | |||
b1bc792b56 | |||
d4b8a86509 | |||
642e7e5c46 | |||
4650d25a53 | |||
7551a28f1a | |||
e51a48fe4c | |||
22c6c09daf | |||
e70a2f21dd | |||
97e6fb6835 | |||
14a7b9f794 | |||
fa1ec48549 | |||
f7f06c5ad4 | |||
e23004df52 | |||
fe70c2647a | |||
ada1e642c5 | |||
817c79ca77 | |||
a97a2266d3 | |||
8b3c808cb0 | |||
2d929e73ec | |||
681d06386d | |||
77b31ab42f | |||
5b4eb0c6d7 | |||
5c25248582 | |||
64ad74acbe | |||
02997f473a | |||
abda569b55 | |||
447018b54b | |||
0a200aff70 | |||
883e1c1541 | |||
aabe83cf63 | |||
fbd6851860 | |||
d6dae0cc85 | |||
376d508934 | |||
a00cb1de50 | |||
56f3429cc9 | |||
4943c0f887 | |||
00e2d70f05 | |||
e52d71060f | |||
01869f9fa8 | |||
526c51d1a6 | |||
2b4d30d931 | |||
3dc4df2ffa | |||
f830cfca12 | |||
eac5c88893 | |||
abfe175c9e | |||
0fc66daef6 | |||
5a095bb257 | |||
de31aca7a7 | |||
97220dd2ba | |||
e1b0bab9a6 | |||
66a2f9b23a | |||
a8533b2133 | |||
87ac100c66 | |||
487c4d23c1 | |||
8906cdbab8 | |||
88963b438f | |||
4fd4a1d15c | |||
371c8b8a1c | |||
eb87f5f851 | |||
27d227283c | |||
ea9d24be31 | |||
5408abca68 | |||
233cb0f96a | |||
8b28e99373 | |||
923d90bce8 | |||
97fc248e00 | |||
7bd682bb27 | |||
e927342e58 | |||
ae84eb7462 | |||
f89bb8eaf3 | |||
0d0b3a35da | |||
cf7bef58b0 | |||
c65fd31e86 | |||
6600ac7031 | |||
957384ceeb | |||
d3e391d176 | |||
0658d5602e | |||
458213d055 | |||
cd1295a823 | |||
5d302c504e | |||
68faddbf5c | |||
dc9c614da2 | |||
8c1adabe1c | |||
213dabdceb | |||
6ecaa9aebb | |||
cb2e646332 | |||
fef1dee7aa | |||
e14543498c | |||
c25478380c | |||
e819e97f9a | |||
f149ae79c6 | |||
ffe13078e5 | |||
f0d0fe0801 | |||
d630d5baa5 | |||
7cac7c5157 | |||
aba61f22a6 | |||
8475c63a6a | |||
2d09f84182 | |||
ef6e0d8eb8 | |||
db97d73c3b | |||
0e3d0fbec6 | |||
896a1564ef | |||
8b97cf1479 | |||
b5c9f9ed9b | |||
bda120d862 | |||
3177576ad6 | |||
896a0457f8 | |||
caf8e2723d | |||
0107543a33 | |||
7d29636087 | |||
70433e6b73 | |||
3667854a8f | |||
c5c1c9e38e | |||
308823b6ea | |||
c05bad381c | |||
4a961f4ecb | |||
6c8398df9b | |||
ff6e7754ae | |||
28ba179e31 | |||
b96e560c8d | |||
7c95cea3a8 | |||
34501aaae6 | |||
dbfc4c1c16 | |||
301863b105 | |||
ef8dc40492 | |||
6dbd4d969b | |||
3dd0ac1f0a | |||
5b42272365 | |||
1f6c6dbf2f | |||
0012caa4d5 | |||
8499cf84c3 | |||
daf0f472b3 | |||
a3decad4c2 | |||
7b790a3369 | |||
e18920884e | |||
6f8fef4f13 | |||
e295940833 | |||
2ed7622239 | |||
6ce085a21a | |||
e34146fc14 | |||
4e2316c742 | |||
785900f722 | |||
5ce5a87abe | |||
afe5465862 | |||
678d541da7 | |||
f0477e164a | |||
b5002fb46b | |||
82127571b5 | |||
f6e9d1f857 | |||
2cab7c79c3 | |||
bba849909c | |||
cac74c73e1 | |||
f375dbd013 | |||
ea58ef85fc | |||
bf7933714a | |||
564477b8a0 | |||
7e2c04e805 | |||
8fa1539bac | |||
f45281a10a | |||
e9f70293ac | |||
61cb99ea42 | |||
5408a9a72d | |||
22c1a0d030 | |||
8c3007e4b5 | |||
838aa2aaa9 | |||
3285ffba16 | |||
17e8857efc | |||
226cbc7db3 | |||
cc7c7b3321 | |||
70cea03b4b | |||
a027912891 | |||
3bdf669ddf | |||
b94b04c074 | |||
cfc5dd830c | |||
a3097aaf05 | |||
69c3bff086 | |||
50098767fc | |||
de581ea8b3 | |||
9f8a9c6fc7 | |||
ad083ed28f | |||
105ba30ce9 | |||
ee8bf0b3c0 | |||
41262f4265 | |||
c349eb4fa4 | |||
ca958464c4 | |||
d6003ee0ab | |||
bc248e9a15 | |||
42c0171b40 | |||
1a99090b45 | |||
b7eea4f577 | |||
9c62b5867e | |||
2560af731a | |||
86211eb5f0 | |||
a3387b7f48 | |||
94a48e8640 | |||
d8aeb40b49 | |||
52c55d0ee8 | |||
438c2b31e4 | |||
57e308dd46 | |||
c922b5a112 | |||
d552303cd5 | |||
1d4d18d9db | |||
069bbf3ed0 | |||
a4a2d4e56d | |||
d77f409093 | |||
25c709c58e | |||
bc909d1d0f | |||
a6736ff9f2 | |||
894a0f0ee5 | |||
abea92af59 | |||
bcbed2812d | |||
c0b04ca0bc | |||
86dc3e5b07 | |||
c1aa65239e | |||
be5ccf6957 | |||
09067ebdc5 | |||
08697e71fa | |||
90d9a1df3f | |||
a96c149793 | |||
1037cef22e | |||
09948f4403 | |||
788461b7e2 | |||
4f56628566 | |||
bcbf1ccc68 | |||
ae30d7ba40 | |||
9adf41ca2d | |||
1d79d534d9 | |||
60e4197026 | |||
2fabca77b9 | |||
47542b0cb0 | |||
6c60c3e547 | |||
814d389b6e | |||
e81e5fb2b9 | |||
f68cdf3878 | |||
91e0e9e1dd | |||
59c1299168 | |||
27c6afbeb4 | |||
514ba54282 | |||
a11f683e7b | |||
b65b145122 | |||
982bb8b01d | |||
eb7b7581ca | |||
adab6c0728 | |||
609201e109 | |||
54a4e4a67c | |||
aca4604879 | |||
48811cd805 | |||
136f64f4ac | |||
123ee8e06f | |||
a55efbd8b8 | |||
7bf9525353 | |||
3915e1b242 | |||
ed5975d3e5 | |||
1a788e6b0d | |||
d822793229 | |||
b46d0bc48c | |||
65320126c2 | |||
c63b3164bd | |||
dbffa88dc2 | |||
8c5d9d372f | |||
50f8892c6b | |||
3bfbfa8ae0 | |||
8598c87ef4 | |||
33bfc4c24a | |||
3afb744e77 | |||
e92918bbfe | |||
723e8fde93 | |||
507f7ea70a | |||
6b985d56a5 | |||
c8385ad998 | |||
9d21a6f40d | |||
d304f41197 | |||
8d85b839b6 | |||
dd235f38a3 | |||
5306b6dd0c | |||
b09624024b | |||
edc3709451 | |||
e706f3477b | |||
6298cb3999 | |||
878fce6482 | |||
b02bd65871 | |||
ee36aaf163 | |||
ff84506bd5 | |||
0ae33b7e3c | |||
b1dc6239ef | |||
3ce0f1146f | |||
3ec837bfdb | |||
18ff2be9bb | |||
c0d296334c | |||
9a0a2e319c | |||
a0d86ac2bb | |||
99045b2f6a | |||
c34ca36778 | |||
58dd75a1c8 | |||
f995b07876 | |||
101a4aa3cf | |||
65d759316b | |||
19c1773133 | |||
9b3b3d325f | |||
43f4374944 | |||
81e6d13241 | |||
f8e7a37c0d | |||
c686e7ea30 | |||
7e89af8190 | |||
539e8e2cce | |||
aab084866c | |||
0e61a86763 | |||
1c9938ed98 | |||
47c1a0f381 | |||
514529b5d9 | |||
a12dc7d75a | |||
41b53e71e1 | |||
0fb9f3bd6c | |||
81f3f32217 | |||
b35f288794 | |||
4e82cc0861 | |||
c735644c57 | |||
5d479fa0ae | |||
8baedca972 | |||
02aa8e7945 | |||
ee523efcb4 | |||
eef5f7e06d | |||
83402930f2 | |||
bd48c927d0 | |||
b61b8d60b7 | |||
f1fca5abb6 | |||
045ce3c77a | |||
f822066e2a |
3
.clang-format
Normal file
3
.clang-format
Normal file
@ -0,0 +1,3 @@
|
||||
Language: JavaScript
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,11 +1,18 @@
|
||||
.DS_STORE
|
||||
|
||||
# Don’t commit the following directories created by pub.
|
||||
packages
|
||||
pubspec.lock
|
||||
.pub
|
||||
.packages
|
||||
|
||||
/dist/
|
||||
packages/
|
||||
.buildlog
|
||||
node_modules
|
||||
bower_components
|
||||
.pub
|
||||
.DS_STORE
|
||||
|
||||
# Or broccoli working directory
|
||||
tmp
|
||||
|
||||
# Or the files created by dart2js.
|
||||
*.dart.js
|
||||
@ -14,13 +21,26 @@ bower_components
|
||||
*.js.deps
|
||||
*.js.map
|
||||
|
||||
# Or type definitions we mirror from github
|
||||
**/typings/**/*.d.ts
|
||||
**/typings/tsd.cached.json
|
||||
|
||||
# Include when developing application packages.
|
||||
pubspec.lock
|
||||
.c9
|
||||
.idea/
|
||||
*.swo
|
||||
modules/.settings
|
||||
.vscode
|
||||
modules/.vscode
|
||||
|
||||
# Don't check in secret files
|
||||
*secret.js
|
||||
|
||||
# Ignore npm debug log
|
||||
npm-debug.log
|
||||
|
||||
/docs/bower_components/
|
||||
|
||||
# build-analytics
|
||||
.build-analytics
|
||||
|
12
.settings/settings.json
Normal file
12
.settings/settings.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
".git" : true,
|
||||
".idea": true,
|
||||
"node_modules" : true,
|
||||
"bower_components" : true,
|
||||
"packages" : true,
|
||||
"build" : true,
|
||||
"dist" : true,
|
||||
"tmp" : true
|
||||
}
|
||||
}
|
104
.travis.yml
104
.travis.yml
@ -1,38 +1,128 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '0.10'
|
||||
- '4.1.1'
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3sync
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- $HOME/.pub-cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- KARMA_BROWSERS=DartiumWithWebPlatform
|
||||
- E2E_BROWSERS=Dartium
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- ARCH=linux-x64
|
||||
# TODO: change DART_DEV_VERSION to "latest" again once
|
||||
# https://github.com/angular/angular/issues/4467 is solved
|
||||
- DART_DEV_VERSION=1.13.0-dev.3.1
|
||||
- DART_STABLE_VERSION=latest
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
||||
# because those are not visible for pull requests, and those should also be reliable.
|
||||
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# (password is in Valentine)
|
||||
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
matrix:
|
||||
- MODE=js DART_CHANNEL=dev
|
||||
# Dissabled until Dart v1.9 hits stable
|
||||
# - MODE=dart DART_CHANNEL=stable
|
||||
- MODE=dart DART_CHANNEL=dev
|
||||
# Order: slowest build on top, so that we don't hog VMs while waiting for others to complete.
|
||||
- MODE=dart DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=js DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=router DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
|
||||
addons:
|
||||
firefox: "38.0"
|
||||
|
||||
before_install:
|
||||
- echo ${TSDRC} > .tsdrc
|
||||
- export DISPLAY=:99.0
|
||||
- export GIT_SHA=$(git rev-parse HEAD)
|
||||
- ./scripts/ci/init_android.sh
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${ARCH}
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
|
||||
install:
|
||||
# Update npm
|
||||
- npm install -g npm@2.14.5
|
||||
- npm --version
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules || true
|
||||
# Install npm dependecies
|
||||
- npm install
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./scripts/ci/presubmit-queue-setup.sh
|
||||
|
||||
script:
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
|
||||
after_script:
|
||||
- ./scripts/ci/print-logs.sh
|
||||
- ./scripts/ci/after-script.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
# trigger Buildtime Trend Service to parse Travis CI log
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
||||
slack:
|
||||
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
||||
deploy:
|
||||
- provider: gcs
|
||||
# This is for project angular-github-babysitter
|
||||
access_key_id: GOOGIOQTDBEOPBUAWFZQ
|
||||
secret_access_key:
|
||||
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
|
||||
# this bucket has a lifecycle to delete after 90 days:
|
||||
# $ echo '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 90}}]}' > lifecycle.json
|
||||
# $ gsutil lifecycle set lifecycle.json gs://angular2-snapshots
|
||||
bucket: angular2-snapshots
|
||||
# don't delete generated files
|
||||
skip_cleanup: true
|
||||
# serve to public at https://storage.googleapis.com/angular2-snapshots/SHA/dart_stable/dist.tgz
|
||||
acl: public-read
|
||||
# upload the .tgz archive created in scripts/ci/build_and_test.sh
|
||||
local-dir: deploy
|
||||
# create a "subdirectory" for each commit
|
||||
upload-dir: $TRAVIS_COMMIT/dart_stable
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = dart && $DART_CHANNEL = stable"
|
||||
- provider: gcs
|
||||
access_key_id: GOOGIOQTDBEOPBUAWFZQ
|
||||
secret_access_key:
|
||||
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
|
||||
bucket: angular2-snapshots
|
||||
skip_cleanup: true
|
||||
acl: public-read
|
||||
local-dir: deploy
|
||||
upload-dir: $TRAVIS_COMMIT/js
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = js"
|
||||
|
1331
CHANGELOG.md
Normal file
1331
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
31
COMMITTER.md
Normal file
31
COMMITTER.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Pushing changes into the Angular 2 tree
|
||||
|
||||
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
|
||||
for details about how we maintain a linear commit history, and the rules for committing.
|
||||
|
||||
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
||||
Someone with committer access will do the rest.
|
||||
|
||||
## The `PR: merge` label and `presubmit-*` branches
|
||||
|
||||
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
||||
Angular committers and also prevent breakages on master.
|
||||
|
||||
When a PR is ready to merge, a project member in the CoreTeamMember list (see below) can add the special label,
|
||||
`PR: merge`.
|
||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
||||
is notified that the label was added by an authorized person,
|
||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
||||
|
||||
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
|
||||
|
||||
When a Travis build succeeds for a presubmit branch named following the convention,
|
||||
Travis will re-base the commits, merge to master, and close the PR automatically.
|
||||
|
||||
Finally, after merge `mary-poppins` removes the presubmit branch.
|
||||
|
||||
## Administration
|
||||
|
||||
The list of users who can trigger a merge by adding the label is stored in our appengine app datastore.
|
||||
Edit the contents of the [CoreTeamMember Table](
|
||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
@ -18,7 +18,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?
|
||||
|
||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
||||
discussion list or [StackOverflow][stackoverflow]. We are also available on [Gitter][gitter].
|
||||
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||
@ -27,27 +27,16 @@ If you find a bug in the source code or a mistake in the documentation, you can
|
||||
|
||||
## <a name="feature"></a> Want a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
||||
Repository][github]. If you would like to *implement* a new feature then consider what kind of
|
||||
change it is:
|
||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
||||
and we are not ready to accept major contributions ahead of the full release.
|
||||
Please consider what kind of change it is:
|
||||
|
||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||
and help you to craft the change so that it is successfully accepted into the project.
|
||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||
|
||||
## <a name="docs"></a> Want a Doc Fix?
|
||||
If you want to help improve the docs, then consider what kind of improvement it is:
|
||||
|
||||
* For **Major Changes**, it's a good idea to let others know what you're working on to
|
||||
minimize duplication of effort. Before starting, check out the issue queue for
|
||||
issues labeled [#docs](https://github.com/angular/angular/labels/%23docs).
|
||||
Comment on an issue to let others know what you're working on, or [create a new issue](#submit-issue)
|
||||
if your work doesn't fit within the scope of any of the existing doc issues.
|
||||
Please build and test the documentation before [submitting the Pull Request](#submit-pr), to be sure
|
||||
you haven't accidentally introduced any layout or formatting issues. Also ensure that your commit
|
||||
message is labeled "docs" and follows the [Commit Message Guidelines](#commit) given below.
|
||||
* For **Small Changes**, there is no need to file an issue first. Simply [submit a Pull Request](#submit-pr).
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
@ -60,7 +49,6 @@ chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps.
|
||||
@ -71,7 +59,7 @@ chances of your issue being dealt with quickly:
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
* Search [GitHub](https://github.com/angular/angular.dart/pulls) for an open or closed PR
|
||||
* Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
We cannot accept code without this.
|
||||
@ -147,9 +135,9 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
|
||||
|
||||
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||
* All public API methods **must be documented**. (Details TBC).
|
||||
* With the exceptions listed below, we follow the rules contained in
|
||||
[Google's JavaScript Style Guide][js-style-guide]:
|
||||
* Wrap all code at **100 characters**.
|
||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
|
||||
**100 characters**. An automated formatter is available, see
|
||||
[DEVELOPER.md](DEVELOPER.md#clang-format).
|
||||
|
||||
## <a name="commit"></a> Commit Message Guidelines
|
||||
|
||||
@ -169,9 +157,14 @@ format that includes a **type**, a **scope** and a **subject**:
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
### Revert
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
|
||||
@ -180,7 +173,7 @@ Must be one of the following:
|
||||
* **docs**: Documentation only changes
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug or adds a feature
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests
|
||||
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||
@ -205,6 +198,7 @@ The body should include the motivation for the change and contrast this with pre
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
|
232
DEVELOPER.md
232
DEVELOPER.md
@ -3,56 +3,63 @@
|
||||
This document describes how to set up your development environment to build and test Angular, both
|
||||
JS and Dart versions. It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||
|
||||
See the [contributing guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
for how to contribute your own code to
|
||||
* [Prerequisite Software](#prerequisite-software)
|
||||
* [Getting the Sources](#getting-the-sources)
|
||||
* [Environment Variable Setup](#environment-variable-setup)
|
||||
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
* [Running Tests Locally](#running-tests-locally)
|
||||
* [Formatting](#clang-format)
|
||||
* [Project Information](#project-information)
|
||||
* [CI using Travis](#ci-using-travis)
|
||||
* [Transforming Dart code](#transforming-dart-code)
|
||||
* [Debugging](#debugging)
|
||||
|
||||
1. [Prerequisite Software](#prerequisite-software)
|
||||
2. [Getting the Sources](#getting-the-sources)
|
||||
3. [Environment Variable Setup](#environment-variable-setup)
|
||||
4. [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
5. [Running Tests Locally](#running-tests-locally)
|
||||
6. [Project Information](#project-information)
|
||||
7. [CI using Travis](#ci-using-travis)
|
||||
8. [Debugging](#debugging)
|
||||
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
if you'd like to contribute to Angular.
|
||||
|
||||
## Prerequisite Software
|
||||
|
||||
Before you can build and test Angular, you must install and configure the
|
||||
following products on your development machine:
|
||||
|
||||
* [Dart](https://www.dartlang.org) (version `>=1.9.0-dev.8.0`), specifically the Dart-SDK and
|
||||
* [Dart](https://www.dartlang.org) (version ` >=1.12.0 <2.0.0`), specifically the Dart-SDK and
|
||||
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
||||
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
||||
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
||||
download [page for instructions](https://www.dartlang.org/tools/download.html); note that you can
|
||||
download both **stable** and **dev** channel versions from the [download
|
||||
archive](https://www.dartlang.org/tools/download-archive).
|
||||
download [page for instructions](https://www.dartlang.org/tools/download.html).
|
||||
You can also download both **stable** and **dev** channel versions from the [download
|
||||
archive](https://www.dartlang.org/tools/download-archive). In that case, on Windows, Dart must be added
|
||||
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
|
||||
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **Github app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)): the [Github Guide to Installing
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org) which is used to run a development web server, run tests, and
|
||||
generate distributable files. We also use Node's Package Manager (`npm`). Depending on your
|
||||
system, you can install Node either from source or as a pre-packaged bundle.
|
||||
* [Node.js](http://nodejs.org), (version `>=4.1.1 <5`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(version `>=2.14.5 <3.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
|
||||
bleeding edge functionality, built especially for developers (and early adopters).
|
||||
|
||||
* [Bower](http://bower.io/).
|
||||
|
||||
|
||||
## Getting the Sources
|
||||
|
||||
Forking and cloning the Angular repository:
|
||||
Fork and clone the Angular repository:
|
||||
|
||||
1. Login to your Github account or create one by following the instructions given
|
||||
1. Login to your GitHub account or create one by following the instructions given
|
||||
[here](https://github.com/signup/free).
|
||||
2. [Fork](http://help.github.com/forking) the [main Angular
|
||||
repository](https://github.com/angular/angular).
|
||||
3. Clone your fork of the Angular repository and define an `upstream` remote pointing back to
|
||||
the Angular repository that you forked in the first place:
|
||||
the Angular repository that you forked in the first place.
|
||||
|
||||
```shell
|
||||
# Clone your Github repository:
|
||||
# Clone your GitHub repository:
|
||||
git clone git@github.com:<github username>/angular.git
|
||||
|
||||
# Go to the Angular directory:
|
||||
@ -90,32 +97,31 @@ PATH+=":$DART_SDK/bin"
|
||||
|
||||
## Installing NPM Modules and Dart Packages
|
||||
|
||||
Next, install the modules and packages needed to build Angular and run tests:
|
||||
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
|
||||
|
||||
```shell
|
||||
# Install Angular project dependencies (package.json)
|
||||
npm install
|
||||
|
||||
# Ensure protractor has the latest webdriver
|
||||
$(npm bin)/webdriver-manager update
|
||||
|
||||
# Install Dart packages
|
||||
pub get
|
||||
```
|
||||
|
||||
**Optional**: In this document, we make use of project local `npm` package scripts and binaries
|
||||
(stored under `./node_modules/.bin`) by prefixing these command invocations with `$(npm bin)`; in
|
||||
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by
|
||||
globally installing these two packages as follows:
|
||||
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by either:
|
||||
|
||||
*Option 1*: globally installing these two packages as follows:
|
||||
|
||||
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
|
||||
* `npm install -g protractor` (you might need to prefix this command with `sudo`)
|
||||
|
||||
Since global installs can become stale, we avoid their use in these instructions.
|
||||
Since global installs can become stale, and required versions can vary by project, we avoid their
|
||||
use in these instructions.
|
||||
|
||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||
|
||||
## Build commands
|
||||
|
||||
To build Angular and prepare tests run
|
||||
To build Angular and prepare tests, run:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp build
|
||||
@ -124,42 +130,69 @@ $(npm bin)/gulp build
|
||||
Notes:
|
||||
* Results are put in the `dist` folder.
|
||||
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
|
||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`
|
||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`.
|
||||
|
||||
You can selectively build either the JS or Dart versions as follows:
|
||||
|
||||
* `$(npm bin)/gulp build.js`
|
||||
* `$(npm bin)/gulp build.dart`
|
||||
|
||||
Also note that in order for the whole test suite to succeed you will need to generate the type definitions by running:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp docs/typings
|
||||
```
|
||||
|
||||
To clean out the `dist` folder, run:
|
||||
|
||||
To clean out the `dist` folder use:
|
||||
```shell
|
||||
$(npm bin)/gulp clean
|
||||
```
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
### Basic tests
|
||||
### Full test suite
|
||||
|
||||
1. `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e. karma
|
||||
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests
|
||||
that run on Travis.
|
||||
|
||||
You can selectively run either the JS or Dart versions as follows:
|
||||
|
||||
* `$(npm bin)/gulp test.all.js`
|
||||
* `$(npm bin)/gulp test.all.dart`
|
||||
|
||||
### Unit tests
|
||||
|
||||
You can run just the unit tests as follows:
|
||||
|
||||
* `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e.
|
||||
watches the test files for changes and re-runs tests when files are updated).
|
||||
2. `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**
|
||||
3. `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
||||
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
|
||||
* `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
||||
|
||||
If you prefer running tests in "single-run" mode rather than watch mode use
|
||||
If you prefer running tests in "single-run" mode rather than watch mode use:
|
||||
|
||||
* `$(npm bin)/gulp test.unit.js/ci`
|
||||
* `$(npm bin)/gulp test.unit.cjs/ci`
|
||||
* `$(npm bin)/gulp test.unit.dart/ci`
|
||||
|
||||
**Note**: If you want to only run a single test you can alter the test you wish
|
||||
to run by changing `it` to `iit` or `describe` to `ddescribe`. This will only
|
||||
run that individual test and make it much easier to debug. `xit` and `xdescribe`
|
||||
can also be useful to exclude a test and a group of tests respectively.
|
||||
The task updates the dist folder with transpiled code whenever a source or test file changes, and
|
||||
Karma is run against the new output.
|
||||
|
||||
**Note** for transpiler tests: The karma preprocessor is setup in a way so that after every test
|
||||
run the transpiler is reloaded. With that it is possible to make changes to the preprocessor and
|
||||
run the tests without exiting karma (just touch a test file that you would like to run).
|
||||
**Note**: If you want to only run a single test you can alter the test you wish to run by changing
|
||||
`it` to `iit` or `describe` to `ddescribe`. This will only run that individual test and make it
|
||||
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
|
||||
tests respectively.
|
||||
|
||||
### E2e tests
|
||||
**Note**: **watch mode** needs symlinks to work, so if you're using windows, ensure you have the
|
||||
rights to built them in your operating system.
|
||||
|
||||
### E2E tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver).
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver).
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: Dart2JS e2e tests.
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js e2e tests.
|
||||
|
||||
Angular specific command line options when running protractor:
|
||||
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
@ -167,13 +200,79 @@ Angular specific command line options when running protractor:
|
||||
### Performance tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver)
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver)
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: Dart2JS performance tests
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: dart2js performance tests
|
||||
|
||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
## Formatting with <a name="clang-format">clang-format</a>
|
||||
|
||||
We use [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to automatically enforce code
|
||||
style for our TypeScript code. This allows us to focus our code reviews more on the content, and
|
||||
less on style nit-picking. It also lets us encode our style guide in the `.clang-format` file in the
|
||||
repository, allowing many tools and editors to share our settings.
|
||||
|
||||
To check the formatting of your code, run
|
||||
|
||||
gulp check-format
|
||||
|
||||
Note that the continuous build on Travis runs `gulp enforce-format`. Unlike the `check-format` task,
|
||||
this will actually fail the build if files aren't formatted according to the style guide.
|
||||
|
||||
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
|
||||
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
|
||||
to some whitespace difference.
|
||||
|
||||
* Install clang-format with `npm install -g clang-format`.
|
||||
* Use `clang-format -i [file name]` to format a file (or multiple).
|
||||
Note that `clang-format` tries to load a `clang-format` node module close to the sources being
|
||||
formatted, or from the `$CWD`, and only then uses the globally installed one - so the version used
|
||||
should automatically match the one required by the project.
|
||||
Use `clang-format -version` in case you get confused.
|
||||
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives
|
||||
you a command line to format your code.
|
||||
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
|
||||
touched.
|
||||
* You can run this as a **git pre-commit hook** to automatically format your delta regions when you
|
||||
commit a change. In the angular repo, run
|
||||
|
||||
```
|
||||
$ echo -e '#!/bin/sh\nexec git clang-format' > .git/hooks/pre-commit
|
||||
$ chmod u+x !$
|
||||
```
|
||||
|
||||
* **WebStorm** can run clang-format on the current file.
|
||||
1. Under Preferences, open Tools > External Tools.
|
||||
1. Plus icon to Create Tool
|
||||
1. Fill in the form:
|
||||
- Name: clang-format
|
||||
- Description: Format
|
||||
- Synchronize files after execution: checked
|
||||
- Open console: not checked
|
||||
- Show in: Editor menu
|
||||
- Program: [path to clang-format, try `$ echo $(npm config get prefix)/bin/clang-format`]
|
||||
- Parameters: `-i -style=file $FilePath$`
|
||||
- Working directory: `$ProjectFileDir$`
|
||||
* `clang-format` integrations are also available for many popular editors (`vim`, `emacs`,
|
||||
`Sublime Text`, etc.).
|
||||
|
||||
## Generating the API documentation
|
||||
|
||||
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp docs/angular.io
|
||||
```
|
||||
|
||||
You can serve the generated documentation to check how it would render on [angular.io](https://angular.io/):
|
||||
- check out the [angular.io repo](https://github.com/angular/angular.io) locally,
|
||||
- install dependencies as described in the [angular.io README](https://github.com/angular/angular.io/blob/master/README.md),
|
||||
- copy the generated documentation from your local angular repo at `angular/dist/angular.io/partials/api/angular2` to your local angular.io repo at `angular.io/public/docs/js/latest/api`,
|
||||
- run `harp compile` at the root of the angular.io repo to check the generated documentation for errors,
|
||||
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
|
||||
|
||||
## Project Information
|
||||
|
||||
### Folder structure
|
||||
@ -184,16 +283,18 @@ Angular specific command line options when running protractor (e.g. force gc, ..
|
||||
|
||||
### File suffixes
|
||||
|
||||
* `*.js`: javascript files that get transpiled to Dart and EcmaScript 5
|
||||
* `*.es6`: javascript files that get transpiled only to EcmaScript 5
|
||||
* `*.es5`: javascript files that don't get transpiled
|
||||
* `*.dart`: dart files that don't get transpiled
|
||||
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
|
||||
* `*.dart`: Dart files that don't get transpiled
|
||||
|
||||
## CI using Travis
|
||||
|
||||
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
||||
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
||||
|
||||
## Transforming Dart code
|
||||
|
||||
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
|
||||
|
||||
## Debugging
|
||||
|
||||
### Debug the transpiler
|
||||
@ -219,17 +320,18 @@ Notes:
|
||||
|
||||
If you need to debug the tests:
|
||||
|
||||
- add a `debugger;` statement to the test you want to debug (oe the source code),
|
||||
- add a `debugger;` statement to the test you want to debug (or the source code),
|
||||
- execute karma `$(npm bin)/gulp test.js`,
|
||||
- press the top right "DEBUG" button,
|
||||
- open the dev tools and press F5,
|
||||
- the execution halt at the `debugger;` statement
|
||||
- open the DevTools and press F5,
|
||||
- the execution halts at the `debugger;` statement
|
||||
|
||||
**Note (WebStorm users)**:
|
||||
You can create a Karma run config from WebStorm.
|
||||
Then in the "Run" menu, press "Debug 'karma-js.conf.js'", WebStorm will stop in the generated code
|
||||
on the `debugger;` statement.
|
||||
You can then step into the code and add watches.
|
||||
|
||||
1. Create a Karma run config from WebStorm.
|
||||
2. Then in the "Run" menu, press "Debug 'karma-js.conf.js'", and WebStorm will stop in the generated
|
||||
code on the `debugger;` statement.
|
||||
3. You can then step into the code and add watches.
|
||||
|
||||
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
|
||||
the original source files are not supported at the moment.
|
||||
|
||||
|
32
NAMING.md
Normal file
32
NAMING.md
Normal file
@ -0,0 +1,32 @@
|
||||
Naming Conventions in Angular2
|
||||
---
|
||||
|
||||
In general Angular2 should follow TypeScript naming conventions.
|
||||
See: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
|
||||
|
||||
|
||||
Classes:
|
||||
- Example: `Compiler`, `ApplicationMetadata`
|
||||
- Camel case with first letter upper-case
|
||||
- In general prefer single words. (This is so that when appending `Proto` or `Factory` the class
|
||||
is still reasonable to work with.)
|
||||
- Should not end with `Impl` or any other word which describes a specific implementation of an
|
||||
interface.
|
||||
|
||||
|
||||
Interfaces:
|
||||
- Follow the same rules as Classes
|
||||
- Should not have `I` or `Interface` in the name or any other way of identifying it as an interface.
|
||||
|
||||
|
||||
Methods and functions:
|
||||
- Example: `bootstrap`, `someMethod`
|
||||
- Should be camel case with first lower case
|
||||
|
||||
|
||||
Constants
|
||||
- Example: `CORE_DIRECTIVES`
|
||||
- Should be all uppercase with SNAKE_CASE
|
||||
|
||||
|
||||
|
43
README.md
43
README.md
@ -1,15 +1,26 @@
|
||||
Angular [](https://travis-ci.org/angular/angular) [](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://badge.fury.io/js/angular2)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
|
||||
Angular
|
||||
=========
|
||||
|
||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||
|
||||
Angular 2 is currently in **Alpha Preview**. We recommend using Angular 1.X for production
|
||||
Angular 2 is currently in **Developer Preview**. We recommend using Angular 1.X for production
|
||||
applications:
|
||||
|
||||
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
|
||||
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
|
||||
|
||||
## Quickstart
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Setup & Install Angular 2
|
||||
|
||||
@ -18,35 +29,15 @@ Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
## Want to help?
|
||||
|
||||
Want to file a bug, or contribute some code or improve documentation? Excellent! Read up on our
|
||||
guidelines for [contributing][contributing].
|
||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
To see the examples, first build the project as described
|
||||
[here](http://github.com/angular/angular/blob/master/DEVELOPER.md).
|
||||
|
||||
### Hello World Example
|
||||
|
||||
This example consists of three basic pieces - a component, a decorator and a
|
||||
service. They are all constructed via injection. For more information see the
|
||||
comments in the source `modules/examples/src/hello_world/index.js`.
|
||||
|
||||
You can build this example as either JS or Dart app:
|
||||
|
||||
* JS:
|
||||
* `$(npm bin)/gulp serve.js.dev`, and
|
||||
* open `localhost:8000/examples/src/hello_world/` in Chrome.
|
||||
* Dart:
|
||||
* `$(npm bin)/gulp serve/examples.dart`, and
|
||||
* open `localhost:8080/src/hello_world` in Chrome (for dart2js) or
|
||||
[Dartium][dartium] (for Dart VM).
|
||||
|
||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
[dart]: http://www.dartlang.org
|
||||
[dartium]: http://www.dartlang.org/tools/dartium
|
||||
[download]: http://angular.io/download
|
||||
[download]: http://angular.io/download/
|
||||
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
|
4
TOOLS.md
Normal file
4
TOOLS.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Developer Tools for Angular 2
|
||||
|
||||
- [JavaScript](TOOLS_JS.md)
|
||||
- [Dart](TOOLS_DART.md)
|
324
TOOLS_DART.md
Normal file
324
TOOLS_DART.md
Normal file
@ -0,0 +1,324 @@
|
||||
# Developer Tools for Dart
|
||||
|
||||
Use these tools and techniques to increase your app's performance
|
||||
and reliability.
|
||||
|
||||
* [Angular debugging tools](#angular-debugging-tools)
|
||||
* [Code size](#code-size)
|
||||
* [Performance](#performance)
|
||||
|
||||
|
||||
## Angular debugging tools
|
||||
|
||||
Starting with alpha.38, Angular provides a set of debugging tools
|
||||
that are accessible from any browser's developer console.
|
||||
In Chrome, you can get to the dev console by pressing
|
||||
Ctrl + Shift + J (on Mac: Cmd + Opt + J).
|
||||
|
||||
### Enabling the debugging tools
|
||||
|
||||
By default the debugging tools are disabled.
|
||||
Enable the debugging tools as follows:
|
||||
|
||||
```dart
|
||||
import 'package:angular2/tools.dart';
|
||||
|
||||
main() async {
|
||||
var appRef = await bootstrap(Application);
|
||||
enableDebugTools(appRef);
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Change function name to enableDebuggingTools? -->
|
||||
|
||||
|
||||
### Using the debugging tools
|
||||
|
||||
In the browser, open the dev console. The top-level object is called `ng` and
|
||||
contains more specific tools inside it.
|
||||
|
||||
For example, to run the change detection profiler on your app:
|
||||
<!-- QUESTION: is "on your app" accurate?
|
||||
is "run the change detection profiler on your app" the best wording? -->
|
||||
|
||||
```javascript
|
||||
// In the dev console:
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The [Change detection profiler](#change-detection-profiler) section
|
||||
has more details.
|
||||
<!-- Point to API docs when they're published, if they're useful.
|
||||
They should be under
|
||||
http://www.dartdocs.org/documentation/angular2/latest
|
||||
and/or
|
||||
https://angular.io/docs/js/latest/api/. -->
|
||||
|
||||
|
||||
## Code size
|
||||
|
||||
Code must be downloaded, parsed, and executed. Too much code can lead to
|
||||
slow application start-up time, especially on slow networks and low-end devices.
|
||||
The tools and techniques in this section can help you to identify
|
||||
unnecessarily large code and to reduce code size.
|
||||
|
||||
### Finding contributors to code size
|
||||
|
||||
Options for investigating code size include the `--dump-info` dart2js option,
|
||||
ng2soyc, `reflector.trackUsage()`, and code coverage information
|
||||
from the Dart VM.
|
||||
|
||||
#### --dump-info
|
||||
|
||||
The `--dump-info` option of `dart2js` outputs information about what happened
|
||||
during compilation. You can specify `--dump-info` in `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
transformers:
|
||||
...
|
||||
- $dart2js:
|
||||
commandLineOptions:
|
||||
- --dump-info
|
||||
```
|
||||
|
||||
The [Dump Info Visualizer](https://github.com/dart-lang/dump-info-visualizer)
|
||||
can help you analyze the output.
|
||||
For more information, see the
|
||||
[dart2js_info API reference](http://dart-lang.github.io/dart2js_info/doc/api/).
|
||||
|
||||
#### ng2soyc.dart
|
||||
|
||||
[ng2soyc](https://github.com/angular/ng2soyc.dart) is a utility for analyzing
|
||||
code size contributors in Angular 2 applications. It groups code size by
|
||||
library and, assuming your library names follow
|
||||
[standard naming conventions](https://www.dartlang.org/articles/style-guide/#do-prefix-library-names-with-the-package-name-and-a-dot-separated-path)
|
||||
(package.library.sublibrary...), gives the code size breakdown at
|
||||
each level. To reduce noise in the output of very large apps, ng2soyc provides
|
||||
an option to hide libraries that are too small, so you can focus on the biggest
|
||||
contributors.
|
||||
|
||||
#### Track unused reflection data
|
||||
|
||||
<!-- QUESTION: How do you get access to reflector & ReflectionInfo? -->
|
||||
|
||||
Call `reflector.trackUsage()` to track reflection information used
|
||||
by the application. Reflection information (`ReflectionInfo`) is a data
|
||||
structure that stores information about your application that Angular uses for
|
||||
locating DI factories, generated change detectors and other code related to a
|
||||
given type. After exercising your application, call `reflector.listUnusedKeys()`
|
||||
to get a list of types and functions whose reflection information was retained
|
||||
but never used by the application.
|
||||
|
||||
#### Use code coverage to find dead code
|
||||
|
||||
When running in Dartium (or in the Dart VM, in general) you can request code
|
||||
coverage information from the VM. You can either use
|
||||
[observatory](https://www.dartlang.org/tools/observatory/) or download
|
||||
the coverage file and use your own tools to inspect it. Lines of code that are
|
||||
not covered are top candidates for dead code.
|
||||
|
||||
Keep in mind, however, that uncovered code is not sufficient evidence of dead
|
||||
code, only necessary evidence. It is perfectly possible that you simply didn't
|
||||
exercise your application in a way that triggers the execution of uncovered
|
||||
code. A common example is error handling code. Just because your testing never
|
||||
encountered an error does not mean the error won't happen in production. You
|
||||
therefore don't have to rush and remove all the `catch` blocks.
|
||||
|
||||
### Reducing code size
|
||||
|
||||
To reduce code size, you can disable reflection,
|
||||
enable minification, and manually remove dead code.
|
||||
You can also try less safe options such as
|
||||
telling dart2js to trust type annotations.
|
||||
|
||||
|
||||
#### Disable reflection
|
||||
|
||||
`dart:mirrors` allows discovering program metadata at runtime. However, this
|
||||
means that `dart2js` needs to retain that metadata and thus increase the size
|
||||
of resulting JS output. In practice, however, it is possible to extract most
|
||||
metadata necessary for your metaprogramming tasks statically using a
|
||||
transformer and `package:analyzer`, and act on it before compiling to JS.
|
||||
|
||||
#### Enable minification
|
||||
|
||||
Minification shortens all your `longMethodNames` into 2- or 3-letter long
|
||||
symbols. `dart2js` ensures that this kind of renaming is done safely, without
|
||||
breaking the functionality of your programs. You can enable it in `pubspec.yaml`
|
||||
under `$dart2js` transformer:
|
||||
|
||||
```yaml
|
||||
transformers:
|
||||
...
|
||||
- $dart2js:
|
||||
minify: true
|
||||
```
|
||||
|
||||
#### Manually remove dead code
|
||||
|
||||
`dart2js` comes with dead code elimination out-of-the-box. However, it may not
|
||||
always be able to tell if a piece of code could be used. Consider the following
|
||||
example:
|
||||
|
||||
```dart
|
||||
/// This function decides which serialization format to use
|
||||
void setupSerializers() {
|
||||
if (server.doYouSupportProtocolBuffers()) {
|
||||
useProtobufSerializers();
|
||||
} else {
|
||||
useJsonSerializers();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example the application asks the server what kind of serialization
|
||||
format it uses and dynamically chooses one or the other. `dart2js` can't
|
||||
tell whether the server responds with yes or no, so it must retain both
|
||||
kinds of serializers. However, if you know that your server supports
|
||||
protocol buffers, you can remove that `if` block entirely and default to
|
||||
protocol buffers.
|
||||
|
||||
Code coverage (see above) is a good way to find dead code in your app.
|
||||
|
||||
#### Unsafe options
|
||||
|
||||
Dart also provides more aggressive optimization options. However, you have to
|
||||
be careful when using them and as of today the benefits aren't that clear. If
|
||||
your type annotations are inaccurate you may end up with non-Darty runtime
|
||||
behavior, including the classic "undefined is not a function" tautology, as
|
||||
well as the "keep on truckin'" behavior, e.g. `null + 1 == 1` and
|
||||
`{} + [] == 0`.
|
||||
|
||||
`--trust-type-annotations` tells `dart2js` to trust that your type annotations
|
||||
are correct. So if you have a function `foo(Bar bar)` the compiler can omit the
|
||||
check that `bar` is truly `Bar` when calling methods on it.
|
||||
|
||||
`--trust-primitives` tells `dart2js` that primitive types, such as numbers and
|
||||
booleans are never `null` when performing arithmetic, and that your program
|
||||
does not run into range error when operating on lists, letting the compiler
|
||||
remove some of the error checking code.
|
||||
|
||||
Specify these options in `pubspec.yaml`.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
transformers:
|
||||
...
|
||||
- $dart2js:
|
||||
commandLineOptions:
|
||||
- --trust-type-annotations
|
||||
- --trust-primitives
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Change detection profiler
|
||||
|
||||
If your application is janky (it misses frames) or is slow according to other
|
||||
metrics it is important to find the root cause of the issue. Change detection
|
||||
is a phase in Angular's lifecycle that detects changes in values that are
|
||||
bound to UI, and if it finds a change it performs the corresponding UI update.
|
||||
However, sometimes it is hard to tell if the slowness is due to the act of
|
||||
computing the changes being slow, or due to the act of applying those changes
|
||||
to the UI. For your application to be performant it is important that the
|
||||
process of computing changes is very fast. For best results it should be under
|
||||
3 milliseconds in order to leave room for the application logic, the UI updates
|
||||
and browser's rendering pipeline to fit withing the 16 millisecond frame
|
||||
(assuming the 60 FPS target frame rate).
|
||||
|
||||
Change detection profiler repeatedly performs change detection without invoking
|
||||
any user actions, such as clicking buttons or entering text in input fields. It
|
||||
then computes the average amount of time it took to perform a single cycle of
|
||||
change detection in milliseconds and prints it to the console. This number
|
||||
depends on the current state of the UI. You are likely to see different numbers
|
||||
as you go from one screen in your application to another.
|
||||
|
||||
#### Running the profiler
|
||||
|
||||
Enable the debugging tools (see above),
|
||||
then in the dev console enter the following:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The results are printed to the console.
|
||||
|
||||
#### Recording CPU profiles
|
||||
|
||||
To record a profile, pass `{record: true}` to `timeChangeDetection()`:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then open the **Profiles** tab. The recorded profile has the title
|
||||
**Change Detection**. In Chrome, if you record the profile repeatedly, all the
|
||||
profiles are nested under Change Detection.
|
||||
|
||||
#### Interpreting the numbers
|
||||
|
||||
In a properly designed application, repeated attempts to detect changes without
|
||||
any user actions result in no changes to the UI. It is
|
||||
also desirable to have the cost of a user action be proportional to the amount
|
||||
of UI changes required. For example, popping up a menu with 5 items should be
|
||||
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
||||
change detection with no UI updates should be as fast as possible. Ideally the
|
||||
number printed by the profiler should be well below the length of a single
|
||||
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
|
||||
|
||||
#### Investigating slow change detection
|
||||
|
||||
So you found a screen in your application on which the profiler reports a very
|
||||
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
||||
recording while profiling:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then look for hot spots using
|
||||
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
||||
|
||||
#### Reducing change detection cost
|
||||
|
||||
There are many reasons for slow change detection. To gain intuition about
|
||||
possible causes it helps to understand how change detection works. Such a
|
||||
discussion is outside the scope of this document,
|
||||
but here are some key concepts.
|
||||
|
||||
<!-- TODO: link to change detection docs -->
|
||||
|
||||
By default, Angular uses a _dirty checking_ mechanism to find model changes.
|
||||
This mechanism involves evaluating every bound expression that's active on the
|
||||
UI. These usually include text interpolation via `{{expression}}` and property
|
||||
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
||||
costly to compute, they might contribute to slow change detection. A good way to
|
||||
speed things up is to use plain class fields in your expressions and avoid any
|
||||
kind of computation. For example:
|
||||
|
||||
```dart
|
||||
@View(
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
)
|
||||
class FancyButton {
|
||||
// GOOD: no computation, just returns the value
|
||||
bool isEnabled;
|
||||
|
||||
// BAD: computes the final value upon request
|
||||
String _title;
|
||||
String get title => _title.trim().toUpperCase();
|
||||
}
|
||||
```
|
||||
|
||||
Most cases like these can be solved by precomputing the value and storing the
|
||||
final value in a field.
|
||||
|
||||
Angular also supports a second type of change detection: the _push_ model. In
|
||||
this model, Angular does not poll your component for changes. Instead, the
|
||||
component tells Angular when it changes, and only then does Angular perform
|
||||
the update. This model is suitable in situations when your data model uses
|
||||
observable or immutable objects.
|
||||
|
||||
<!-- TODO: link to discussion of push model -->
|
140
TOOLS_JS.md
Normal file
140
TOOLS_JS.md
Normal file
@ -0,0 +1,140 @@
|
||||
# Developer Tools for JavaScript
|
||||
|
||||
Here you will find a collection of tools and tips for keeping your application
|
||||
perform well and contain fewer bugs.
|
||||
|
||||
## Angular debug tools in the dev console
|
||||
|
||||
Angular provides a set of debug tools that are accessible from any browser's
|
||||
developer console. In Chrome the dev console can be accessed by pressing
|
||||
Ctrl + Shift + j.
|
||||
|
||||
### Enabling debug tools
|
||||
|
||||
By default the debug tools are disabled. You can enable debug tools as follows:
|
||||
|
||||
```typescript
|
||||
import 'angular2/tools';
|
||||
|
||||
bootstrap(Application).then((appRef) => {
|
||||
enableDebugTools(appRef);
|
||||
});
|
||||
```
|
||||
|
||||
### Using debug tools
|
||||
|
||||
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
|
||||
top level object is called `ng` and contains more specific tools inside it.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Change detection profiler
|
||||
|
||||
If your application is janky (it misses frames) or is slow according to other
|
||||
metrics it is important to find the root cause of the issue. Change detection
|
||||
is a phase in Angular's lifecycle that detects changes in values that are
|
||||
bound to UI, and if it finds a change it performs the corresponding UI update.
|
||||
However, sometimes it is hard to tell if the slowness is due to the act of
|
||||
computing the changes being slow, or due to the act of applying those changes
|
||||
to the UI. For your application to be performant it is important that the
|
||||
process of computing changes is very fast. For best results it should be under
|
||||
3 milliseconds in order to leave room for the application logic, the UI updates
|
||||
and browser's rendering pipeline to fit withing the 16 millisecond frame
|
||||
(assuming the 60 FPS target frame rate).
|
||||
|
||||
Change detection profiler repeatedly performs change detection without invoking
|
||||
any user actions, such as clicking buttons or entering text in input fields. It
|
||||
then computes the average amount of time it took to perform a single cycle of
|
||||
change detection in milliseconds and prints it to the console. This number
|
||||
depends on the current state of the UI. You will likely see different numbers
|
||||
as you go from one screen in your application to another.
|
||||
|
||||
#### Running the profiler
|
||||
|
||||
Enable debug tools (see above), then in the dev console enter the following:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The results will be printed to the console.
|
||||
|
||||
#### Recording CPU profile
|
||||
|
||||
Pass `{record: true}` an argument:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then open the "Profiles" tab. You will see the recorded profile titled
|
||||
"Change Detection". In Chrome, if you record the profile repeatedly, all the
|
||||
profiles will be nested under "Change Detection".
|
||||
|
||||
#### Interpreting the numbers
|
||||
|
||||
In a properly-designed application repeated attempts to detect changes without
|
||||
any user actions should result in no changes to be applied on the UI. It is
|
||||
also desirable to have the cost of a user action be proportional to the amount
|
||||
of UI changes required. For example, popping up a menu with 5 items should be
|
||||
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
||||
change detection with no UI updates should be as fast as possible. Ideally the
|
||||
number printed by the profiler should be well below the length of a single
|
||||
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
|
||||
|
||||
#### Investigating slow change detection
|
||||
|
||||
So you found a screen in your application on which the profiler reports a very
|
||||
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
||||
recording while profiling:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then look for hot spots using
|
||||
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
||||
|
||||
#### Reducing change detection cost
|
||||
|
||||
There are many reasons for slow change detection. To gain intuition about
|
||||
possible causes it would help to understand how change detection works. Such a
|
||||
discussion is outside the scope of this document (TODO link to docs), but here
|
||||
are some key concepts in brief.
|
||||
|
||||
By default Angular uses "dirty checking" mechanism for finding model changes.
|
||||
This mechanism involves evaluating every bound expression that's active on the
|
||||
UI. These usually include text interpolation via `{{expression}}` and property
|
||||
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
||||
costly to compute they could contribute to slow change detection. A good way to
|
||||
speed things up is to use plain class fields in your expressions and avoid any
|
||||
kinds of computation. Example:
|
||||
|
||||
```typescript
|
||||
@View({
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
})
|
||||
class FancyButton {
|
||||
// GOOD: no computation, just return the value
|
||||
isEnabled: boolean;
|
||||
|
||||
// BAD: computes the final value upon request
|
||||
_title: String;
|
||||
get title(): String { return this._title.trim().toUpperCase(); }
|
||||
}
|
||||
```
|
||||
|
||||
Most cases like these could be solved by precomputing the value and storing the
|
||||
final value in a field.
|
||||
|
||||
Angular also supports a second type of change detection - the "push" model. In
|
||||
this model Angular does not poll your component for changes. Instead, the
|
||||
component "tells" Angular when it changes and only then does Angular perform
|
||||
the update. This model is suitable in situations when your data model uses
|
||||
observable or immutable objects (also a discussion for another time).
|
168
TRIAGE_AND_LABELS.md
Normal file
168
TRIAGE_AND_LABELS.md
Normal file
@ -0,0 +1,168 @@
|
||||
# Triage Process and Github Labels for Angular 2
|
||||
|
||||
This document describes how the Angular team uses labels and milestones to triage issues on github.
|
||||
|
||||
# Issues and PRs
|
||||
## Triaged vs Untriaged Issues
|
||||
|
||||
Every triaged issue must have four attributes assigned to it:
|
||||
|
||||
* `priority` -- P0 through P4. P0 issues are "drop everything and do this now". P4 are nice to have.
|
||||
* `component` -- Which area of Angular knowledge this relates to.
|
||||
* `effort` -- Rough assessment of how much work this issue is. E.g. `effort: easy` means
|
||||
"probably a few hours of work".
|
||||
* `type` -- Whether this issue is a bug, feature, or other kind of task.
|
||||
|
||||
Untriaged issues are any issues in the queue that don't yet have these four attributes.
|
||||
|
||||
You can view a report of untriaged issues here, in our
|
||||
[Angular Triage Dashboard](http://mhevery.github.io/github_issues/).
|
||||
|
||||
Issues should also have a clear action to complete that can be addressed or resolved within the
|
||||
scope of Angular 2. We'll close issues that don't meet these criteria.
|
||||
|
||||
### Assigning Issues to Milestones
|
||||
|
||||
Any issue that is being worked on must have:
|
||||
|
||||
* An `assignee`: The person doing the work.
|
||||
* A `Milestone`: When we expect to complete this work.
|
||||
|
||||
We aim to only have at most three milestones open at a time:
|
||||
|
||||
* Closing Milestone: A milestone with a very small number of issues, about to release.
|
||||
* Current Milestone: Work that we plan to complete within one week.
|
||||
* Next Milestone: Work that is > 1 week but current for the team.
|
||||
|
||||
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
|
||||
consists of all issues that have been triaged but do not have an assignee or milestone.
|
||||
|
||||
## Triaged vs Untriaged PRs
|
||||
|
||||
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
||||
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
||||
|
||||
Every triaged PR must have a `pr_action` label assigned to it and an assignee:
|
||||
|
||||
* `pr_action: review` -- work is complete and comment is needed from the assignee.
|
||||
* `pr_action: cleanup` -- more work is needed from the current assignee.
|
||||
* `pr_action: discuss` -- discussion is needed, to be led by the current assignee.
|
||||
* `pr_action: merge` -- the PR should be merged. Add this to a PR when you would like to
|
||||
trigger automatic merging following a successful build. This is described in [COMMITTER.md](COMMITTER.md).
|
||||
|
||||
In addition, PRs can have the following states:
|
||||
|
||||
* `pr_state: LGTM` -- PR may have outstanding changes but does not require further review.
|
||||
* `pr_state: WIP` -- PR is experimental or rapidly changing. Not ready for review or triage.
|
||||
* `pr_state: blocked` -- PR is blocked on an issue or other PR. Not ready for review or triage.
|
||||
|
||||
Note that an LGTM state does not mean a PR is ready to merge: for example, a reviewer might set the
|
||||
LGTM state but request a minor tweak that doesn't need further review, e.g., a rebase or small
|
||||
uncontroversial change.
|
||||
|
||||
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
|
||||
PR to land.
|
||||
|
||||
Victor (`vsavkin`) and Tobias (`tbosch`) are owners of the PR queue. Here is a list of [current
|
||||
untriaged PRs](https://github.com/angular/angular/pulls?utf8=%E2%9C%93&q=is%3Aopen+no%3Amilestone+is%3Apr+-label%3A%22pr_action%3A+cleanup%22+-label%3A%22pr_action%3A+merge%22+-label%3A%22pr_action%3A+review%22+-label%3A%22pr_action%3A+discuss%22+-label%3A%22pr_state%3A+blocked%22+-label%3A%22pr_state%3A+WIP%22+).
|
||||
|
||||
# Prioritization of Work
|
||||
|
||||
What should you be working on?
|
||||
|
||||
1. Any PRs that are assigned to you that don't have `pr_state: WIP` or `pr_state: blocked`
|
||||
1. Any issues that are assigned to you in the lowest-numbered Milestone
|
||||
1. Any issues that are assigned to you in any Milestone
|
||||
|
||||
If there are no issues assigned to you in any Milestone, pick an issue, self-assign it, and add
|
||||
it to the most appropriate Milestone based on effort.
|
||||
|
||||
Here are some suggestions for what to work on next:
|
||||
|
||||
* Filter for issues in a component that you are knowledgeable about, and pick something that has a
|
||||
high priority.
|
||||
* Filter for any small effort task that has the special `cust: GT` or `cust:Ionic` tags,
|
||||
and priority > P3.
|
||||
* Add a new task that's really important, add `component`, `priority`, `effort`, `type` and
|
||||
assign it to yourself and the most appropriate milestone.
|
||||
|
||||
# Labels Used in Triage
|
||||
|
||||
## Priority
|
||||
How urgent is this issue? We use priority to determine what should be worked on in each new
|
||||
milestone.
|
||||
|
||||
* `P0: critical` -- drop everything to work on this
|
||||
* `P1: urgent` -- resolve quickly in the current milestone. people are blocked
|
||||
* `P2: required` -- needed for development but not urgent yet. workaround exists, or e.g. new API
|
||||
* `P3: important` -- must complete before Angular 2 is ready for release
|
||||
* `P4: nice to have` -- a good idea, but maybe not until after release
|
||||
|
||||
|
||||
## Effort
|
||||
Rough, non-binding estimate of how much work this issue represents. Please change this assessment
|
||||
for anything you're working on to better reflect reality.
|
||||
|
||||
* `effort: easy` -- straightforward issue that can be resolved in a few hours, e.g. < 1 day of work.
|
||||
* `effort: medium` -- issue that will be a few days of work. Can be completed within a single
|
||||
milestone.
|
||||
* `effort: tough` -- issue that will likely take more than 1 milestone to complete.
|
||||
|
||||
<!-- We don't like these label names as
|
||||
they're not absolute (what is one developer-hour, really?) but decided it wasn't worth arguing
|
||||
over terms. -->
|
||||
|
||||
## Component
|
||||
Which area of Angular knowledge is this issue most closely related to? Helpful when deciding what
|
||||
to work on next.
|
||||
|
||||
* `comp: benchpress` -- benchmarks and performance testing → *tbosch*, *crossj*
|
||||
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks → *iminar*, *caitp*
|
||||
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart → *mprobst*, *alexeagle*
|
||||
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) →
|
||||
*mhevery*
|
||||
* `comp: core/animations` -- animations framework → *matsko*
|
||||
* `comp: core/change_detection` -- change detection → *vsavkin*
|
||||
* `comp: core/di` -- dependency injection → *vicb*, *rkirov*
|
||||
* `comp: core/directives` -- directives
|
||||
* `comp: core/forms` -- forms → *vsavkin*
|
||||
* `comp: core/pipes` -- pipes
|
||||
* `comp: core/view` -- runtime processing of the `View`s
|
||||
* `comp: core/view/compiler` -- static analysis of the templates which generate `ProtoView`s.
|
||||
* `comp: core/testbed` -- e2e tests and support for them
|
||||
* `comp: core/webworker` -- core web worker infrastructure
|
||||
* `comp: dart-transformer` -- Dart transforms → *kegluneq*, *jakemac*
|
||||
* `comp: data-access` -- → *jeffbcross*
|
||||
* `comp: docs` -- API docs and doc generation → *naomiblack*, *petebacondarwin*
|
||||
* `comp: material-components` -- Angular Material components built in Angular 2 → *jelbourn*
|
||||
* `comp: router` -- Component Router → *btford*, *igorminar*, *matsko*
|
||||
* `comp: wrenchjs`
|
||||
|
||||
## Type
|
||||
What kind of problem is this?
|
||||
|
||||
* `type RFC / discussion / question`
|
||||
* `type bug`
|
||||
* `type chore`
|
||||
* `type feature`
|
||||
* `type performance`
|
||||
* `type refactor`
|
||||
|
||||
## Special Labels
|
||||
|
||||
### action:design
|
||||
More active discussion is needed before the issue can be worked on further. Typically used for
|
||||
`type: feature` or `type: RFC/discussion/question`
|
||||
|
||||
[See all issues that need discussion](https://github.com/angular/angular/labels/action:%20Design)
|
||||
|
||||
### cla
|
||||
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
||||
`cla:yes` should be merged into master.
|
||||
|
||||
### cust
|
||||
This is an issue causing user pain for early adopter customers `cust: GT` or `cust: Ionic`.
|
||||
|
||||
### WORKS_AS_INTENDED
|
||||
|
||||
Only used on closed issues, to indicate to the reporter why we closed it.
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"name": "angular2",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"polymer": "dart-lang/polymer_js#0.8.0-preview"
|
||||
}
|
||||
|
@ -1,366 +0,0 @@
|
||||
|
||||
.hide { display: none !important; }
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3f51b5;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 20px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: $baseline-grid ($baseline-grid * 2);
|
||||
border-top: 1px solid #ddd;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
border-bottom: 2px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.md-sidenav-inner {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.layout-content,
|
||||
.doc-content {
|
||||
max-width: 864px;
|
||||
margin: auto;
|
||||
}
|
||||
.layout-label {
|
||||
width: 120px;
|
||||
}
|
||||
.layout-content code.highlight {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
background: none;
|
||||
border-width: 0;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
color: #333;
|
||||
font-size: inherit;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
opacity: 1;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 0px 28px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
-webkit-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
-webkit-transition-property: max-height, background-color, opacity;
|
||||
-moz-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
-moz-transition-property: max-height, background-color, opacity;
|
||||
transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
transition-property: max-height, background-color, opacity;
|
||||
}
|
||||
.menu-item.ng-hide {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
.menu-item:hover {
|
||||
color: #999;
|
||||
}
|
||||
.menu-item:focus {
|
||||
font-weight: bold;
|
||||
}
|
||||
.menu-item.menu-title {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
padding-left: 16px;
|
||||
text-align: left;
|
||||
text-transform: uppercase;
|
||||
transition: color 0.35s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
}
|
||||
.menu-item.menu-title:hover,
|
||||
.menu-item.menu-title.active {
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
.app-toolbar .md-toolbar-tools h3 {
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
border-radius: 4px;
|
||||
margin-top: 16px;
|
||||
-webkit-transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
|
||||
transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
|
||||
position: relative;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.demo-source-tabs {
|
||||
z-index: 1;
|
||||
-webkit-transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
max-height: 448px;
|
||||
min-height: 448px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
md-tabs.demo-source-tabs md-tab,
|
||||
md-tabs.demo-source-tabs .md-header {
|
||||
background-color: #444444 !important;
|
||||
}
|
||||
|
||||
|
||||
md-tabs.demo-source-tabs md-tab-label {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
md-tabs.demo-source-tabs .active md-tab-label {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.demo-source-tabs.ng-hide {
|
||||
max-height: 0px;
|
||||
min-height: 0px;
|
||||
}
|
||||
.demo-source-tabs {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
.demo-content {
|
||||
position: relative;
|
||||
overflow:hidden;
|
||||
min-height: 448px;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -moz-box;
|
||||
display: -moz-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.small-demo .demo-source-tabs:not(.ng-hide) {
|
||||
min-height: 224px;
|
||||
max-height: 224px;
|
||||
}
|
||||
.small-demo .demo-content {
|
||||
min-height: 128px;
|
||||
}
|
||||
.demo-content > * {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-moz-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.demo-content > div[layout-fill] {
|
||||
min-height: 448px;
|
||||
}
|
||||
.small-demo .demo-content > div[layout-fill] {
|
||||
min-height: 224px;
|
||||
}
|
||||
.small-demo .demo-toolbar,
|
||||
.small-demo .md-toolbar-tools {
|
||||
min-height: 48px;
|
||||
max-height: 48px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.show-source md-toolbar.demo-toolbar {
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.36);
|
||||
}
|
||||
.demo-toolbar .md-button {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
md-toolbar.demo-toolbar,
|
||||
.demo-source-tabs md-tab,
|
||||
.demo-source-tabs .tabs-header {
|
||||
background: #E0E0E0 !important;
|
||||
color: #616161;
|
||||
}
|
||||
md-toolbar.demo-toolbar md-tab-label {
|
||||
color: #99E4EE
|
||||
}
|
||||
md-toolbar.demo-toolbar .md-button:hover,
|
||||
md-toolbar.demo-toolbar .md-button:focus,
|
||||
md-toolbar.demo-toolbar .md-button.active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
md-toolbar.demo-toolbar .md-button {
|
||||
-webkit-transition: all 0.3s linear;
|
||||
-moz-transition: all 0.3s linear;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
.demo-source-container {
|
||||
display: block;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #f6f6f6;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.demo-source-content {
|
||||
height: 400px;
|
||||
}
|
||||
.demo-source-content,
|
||||
.demo-source-content pre,
|
||||
.demo-source-content code {
|
||||
background: #f6f6f6;
|
||||
font-family: monospace;
|
||||
}
|
||||
.demo-source-content pre {
|
||||
max-width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.show-source div[demo-include] {
|
||||
border-top: #ddd solid 2px;
|
||||
}
|
||||
|
||||
|
||||
.menu-separator-icon {
|
||||
margin: 0;
|
||||
}
|
||||
.menu-module-name {
|
||||
opacity: 0.6;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/************
|
||||
* DOCS
|
||||
************/
|
||||
.api-options-bar .md-button {
|
||||
margin: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
.api-options-bar .md-button:hover,
|
||||
.api-options-bar .md-button:focus {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.api-options-bar.with-icon md-icon {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: 2px;
|
||||
}
|
||||
.api-options-bar.with-icon .md-button span {
|
||||
margin-left: 22px;
|
||||
}
|
||||
|
||||
.api-params-item {
|
||||
min-height: 72px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.api-params-label {
|
||||
margin-right: 8px;
|
||||
text-align: center;
|
||||
margin-top: 14px;
|
||||
-webkit-align-self: flex-start;
|
||||
-moz-align-self: flex-start;
|
||||
-ms-flex-item-align: start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.api-params-title {
|
||||
color: #888;
|
||||
}
|
||||
code.api-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
ul li {
|
||||
margin-top: 3px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
ul li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.layout-title {
|
||||
color: #999999;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.api-params-content ul {
|
||||
padding-left: 4px;
|
||||
}
|
||||
ul.methods > li {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
ul.methods .method-function-syntax {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
}
|
||||
ul.methods li h3 {
|
||||
/* border-bottom: 1px solid #eee; */
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
ul.methods > li {
|
||||
padding-left: 0;
|
||||
border-left: none;
|
||||
list-style: default;
|
||||
}
|
||||
ul.methods .method-function-syntax {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.version {
|
||||
padding-left: 10px;
|
||||
text-decoration: underline;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.demo-source-container pre,
|
||||
.demo-source-container code {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
md-content.demo-source-container > hljs > pre > code.highlight {
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
|
||||
.extraPad {
|
||||
padding-left:32px !important;
|
||||
padding-right:32px !important;
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
/* GitHub Theme */
|
||||
.prettyprint {
|
||||
background: white;
|
||||
font-family: Menlo, 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-text * {
|
||||
color: #333333!important;
|
||||
}
|
||||
|
||||
.pln {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
.str {
|
||||
color: #dd1144;
|
||||
}
|
||||
|
||||
.kwd {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.com {
|
||||
color: #999988;
|
||||
}
|
||||
|
||||
.typ {
|
||||
color: #445588;
|
||||
}
|
||||
|
||||
.lit {
|
||||
color: #445588;
|
||||
}
|
||||
|
||||
.pun {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.opn {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.clo {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: navy;
|
||||
}
|
||||
|
||||
.atn {
|
||||
color: teal;
|
||||
}
|
||||
|
||||
.atv {
|
||||
color: #dd1144;
|
||||
}
|
||||
|
||||
.dec {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.var {
|
||||
color: teal;
|
||||
}
|
||||
|
||||
.fun {
|
||||
color: #990000;
|
||||
}
|
||||
}
|
||||
@media print, projection {
|
||||
.str {
|
||||
color: #006600;
|
||||
}
|
||||
|
||||
.kwd {
|
||||
color: #006;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.com {
|
||||
color: #600;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.typ {
|
||||
color: #404;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lit {
|
||||
color: #004444;
|
||||
}
|
||||
|
||||
.pun, .opn, .clo {
|
||||
color: #444400;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: #006;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.atn {
|
||||
color: #440044;
|
||||
}
|
||||
|
||||
.atv {
|
||||
color: #006600;
|
||||
}
|
||||
}
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* IE indents via margin-left */
|
||||
li.L0,
|
||||
li.L1,
|
||||
li.L2,
|
||||
li.L3,
|
||||
li.L4,
|
||||
li.L5,
|
||||
li.L6,
|
||||
li.L7,
|
||||
li.L8,
|
||||
li.L9 {
|
||||
/* */
|
||||
}
|
||||
|
||||
/* Alternate shading for lines */
|
||||
li.L1,
|
||||
li.L3,
|
||||
li.L5,
|
||||
li.L7,
|
||||
li.L9 {
|
||||
/* */
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Docs</title>
|
||||
<base href="/">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/lib/angular-material/angular-material.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/prettify-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/app.css">
|
||||
|
||||
<script src="/lib/hammerjs/hammer.js"></script>
|
||||
<script src="/lib/google-code-prettify/src/prettify.js"></script>
|
||||
<script src="/lib/google-code-prettify/src/lang-css.js"></script>
|
||||
<script src="/lib/angular/angular.js"></script>
|
||||
<script src="/lib/angular-animate/angular-animate.js"></script>
|
||||
<script src="/lib/angular-aria/angular-aria.js"></script>
|
||||
<script src="/lib/angular-material/angular-material.js"></script>
|
||||
<script src="/js/navigation-modules.js"></script>
|
||||
<script src="/js/navigation-guides.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
<script src="/js/code.js"></script>
|
||||
</head>
|
||||
<body ng-app="app" ng-controller="NavController as nav" layout="column">
|
||||
|
||||
<md-toolbar md-scroll-shrink>
|
||||
<h1 class="md-toolbar-tools">Angular V2</h1>
|
||||
</md-toolbar>
|
||||
|
||||
<section layout="row">
|
||||
|
||||
<md-content>
|
||||
<h2>Navigation</h2>
|
||||
<section ng-repeat="area in nav.areas">
|
||||
<h3>{{ area.name }}</h3>
|
||||
<md-list>
|
||||
<md-item ng-repeat="section in area.sections">
|
||||
<h3><a href="{{section.path}}">{{section.name}}</a></h3>
|
||||
<ul>
|
||||
<li ng-repeat="page in section.pages">
|
||||
<a href="{{page.path}}">{{ page.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</md-item>
|
||||
</md-list>
|
||||
</section>
|
||||
</md-content>
|
||||
|
||||
|
||||
<md-content class="md-padding">
|
||||
<ng-include src="nav.currentPage.partial"></ng-include>
|
||||
</md-content>
|
||||
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
@ -1,47 +0,0 @@
|
||||
angular.module('app', ['ngMaterial', 'navigation-modules', 'navigation-guides', 'code'])
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
})
|
||||
|
||||
.controller('NavController', ['$scope', '$location', 'MODULES', 'GUIDES',
|
||||
function($scope, $location, MODULES, GUIDES) {
|
||||
var that = this;
|
||||
|
||||
this.areas = [
|
||||
{ name: 'Guides', sections: [ { pages: GUIDES.pages } ] },
|
||||
{ name: 'Modules', sections: MODULES.sections }
|
||||
];
|
||||
|
||||
this.updateCurrentPage = function(path) {
|
||||
path = path.replace(/^\//, '');
|
||||
console.log('path', path);
|
||||
this.currentPage = null;
|
||||
|
||||
this.areas.forEach(function(area) {
|
||||
area.sections.forEach(function(section) {
|
||||
|
||||
// Short-circuit out if the page has been found
|
||||
if ( that.currentPage ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.path === path) {
|
||||
console.log('found!');
|
||||
that.currentPage = section;
|
||||
} else {
|
||||
section.pages.forEach(function(page) {
|
||||
if (page.path === path) {
|
||||
that.currentPage = page;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch(
|
||||
function getLocationPath() { return $location.path(); },
|
||||
function handleLocationPathChange(path) { that.updateCurrentPage(path); }
|
||||
);
|
||||
}]);
|
@ -1,15 +0,0 @@
|
||||
angular.module('code', [])
|
||||
|
||||
.directive('code', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
terminal: true,
|
||||
compile: function(element) {
|
||||
var linenums = element.hasClass('linenum');
|
||||
var match = /lang-(\S+)/.exec(element[0].className);
|
||||
var lang = match && match[1];
|
||||
var html = element.html();
|
||||
element.html(window.prettyPrintOne(html, lang, linenums));
|
||||
}
|
||||
};
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "angular-docs",
|
||||
"main": "index.js",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
"authors": [],
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular-material": "~0.6.0",
|
||||
"google-code-prettify": "~1.0.3"
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
require('../../tools/transpiler/index.js').init();
|
||||
|
||||
var Package = require('dgeni').Package;
|
||||
var jsdocPackage = require('dgeni-packages/jsdoc');
|
||||
var nunjucksPackage = require('dgeni-packages/nunjucks');
|
||||
var path = require('canonical-path');
|
||||
|
||||
var PARTIAL_PATH = 'partials';
|
||||
var MODULES_DOCS_PATH = PARTIAL_PATH + '/modules';
|
||||
var GUIDES_PATH = PARTIAL_PATH + '/guides';
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
|
||||
// Register the services and file readers
|
||||
.factory(require('./services/modules'))
|
||||
.factory(require('./services/atParser'))
|
||||
.factory(require('./services/getJSDocComment'))
|
||||
.factory(require('./services/SourceFile'))
|
||||
.factory(require('./services/TraceurParser'))
|
||||
.factory(require('./services/traceurOptions'))
|
||||
.factory(require('./services/ParseTreeVisitor'))
|
||||
.factory(require('./services/AttachCommentTreeVisitor'))
|
||||
.factory(require('./services/ExportTreeVisitor'))
|
||||
|
||||
.factory(require('./readers/atScript'))
|
||||
.factory(require('./readers/ngdoc'))
|
||||
|
||||
.factory('EXPORT_DOC_TYPES', function() {
|
||||
return [
|
||||
'class',
|
||||
'function',
|
||||
'var',
|
||||
'const'
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/generateDocsFromComments'))
|
||||
.processor(require('./processors/processModuleDocs'))
|
||||
.processor(require('./processors/processClassDocs'))
|
||||
.processor(require('./processors/generateNavigationDoc'))
|
||||
.processor(require('./processors/extractTitleFromGuides'))
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'info';
|
||||
})
|
||||
|
||||
|
||||
// Configure file reading
|
||||
.config(function(readFilesProcessor, atScriptFileReader, ngdocFileReader) {
|
||||
readFilesProcessor.fileReaders = [atScriptFileReader, ngdocFileReader];
|
||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{ include: 'modules/*/*.js', basePath: 'modules' },
|
||||
{ include: 'modules/*/src/**/*.js', basePath: 'modules' },
|
||||
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
|
||||
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/docs';
|
||||
})
|
||||
|
||||
|
||||
// Configure rendering
|
||||
.config(function(templateFinder, templateEngine) {
|
||||
|
||||
// Nunjucks and Angular conflict in their template bindings so change Nunjucks
|
||||
templateEngine.config.tags = {
|
||||
variableStart: '{$',
|
||||
variableEnd: '$}'
|
||||
};
|
||||
|
||||
templateFinder.templateFolders
|
||||
.unshift(path.resolve(__dirname, 'templates'));
|
||||
|
||||
templateFinder.templatePatterns = [
|
||||
'${ doc.template }',
|
||||
'${ doc.id }.${ doc.docType }.template.html',
|
||||
'${ doc.id }.template.html',
|
||||
'${ doc.docType }.template.html',
|
||||
'common.template.html'
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Configure ids and paths
|
||||
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
idTemplate: '${moduleDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['member'],
|
||||
idTemplate: '${classDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['guide'],
|
||||
getId: function(doc) {
|
||||
return doc.fileInfo.relativePath
|
||||
// path should be relative to `modules` folder
|
||||
.replace(/.*\/?modules\//, '')
|
||||
// path should not include `/docs/`
|
||||
.replace(/\/docs\//, '/')
|
||||
// path should not have a suffix
|
||||
.replace(/\.\w*$/, '');
|
||||
},
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['module'],
|
||||
pathTemplate: '${id}',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
pathTemplate: '${moduleDoc.path}/${name}',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${path}/index.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['member'],
|
||||
pathTemplate: '${classDoc.path}/${name}',
|
||||
getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc
|
||||
});
|
||||
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['guide'],
|
||||
pathTemplate: '${id}',
|
||||
outputPathTemplate: GUIDES_PATH + '/${id}.html'
|
||||
});
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function extractTitleFromGuides() {
|
||||
|
||||
return {
|
||||
$runAfter: ['processing-docs'],
|
||||
$runBefore: ['docs-processed'],
|
||||
$process: function(docs) {
|
||||
_(docs).forEach(function(doc) {
|
||||
if (doc.docType === 'guide') {
|
||||
doc.name = doc.name || getNameFromHeading(doc.description);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
function getNameFromHeading(text) {
|
||||
var match = /^\s*#\s*(.*)/.exec(text);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function generateDocsFromComments(log) {
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
$process: function(docs) {
|
||||
var commentDocs = [];
|
||||
docs = _.filter(docs, function(doc) {
|
||||
if (doc.docType !== 'atScriptFile') {
|
||||
return true;
|
||||
} else {
|
||||
_.forEach(doc.fileInfo.comments, function(comment) {
|
||||
|
||||
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
|
||||
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
|
||||
|
||||
// Create a doc from this comment
|
||||
commentDocs.push({
|
||||
fileInfo: doc.fileInfo,
|
||||
startingLine: comment.range.start.line,
|
||||
endingLine: comment.range.end.line,
|
||||
content: commentBody,
|
||||
codeTree: comment.treeAfter,
|
||||
docType: 'atScriptDoc'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return docs.concat(commentDocs);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,66 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function generateNavigationDoc() {
|
||||
|
||||
return {
|
||||
$runAfter: ['docs-processed'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
var modulesDoc = {
|
||||
value: { sections: [] },
|
||||
moduleName: 'navigation-modules',
|
||||
serviceName: 'MODULES',
|
||||
template: 'data-module.template.js',
|
||||
outputPath: 'js/navigation-modules.js'
|
||||
};
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'module' ) {
|
||||
var moduleNavItem = {
|
||||
path: doc.path,
|
||||
partial: doc.outputPath,
|
||||
name: doc.id,
|
||||
type: 'module',
|
||||
pages: []
|
||||
};
|
||||
|
||||
modulesDoc.value.sections.push(moduleNavItem);
|
||||
|
||||
_.forEach(doc.exports, function(exportDoc) {
|
||||
var exportNavItem = {
|
||||
path: exportDoc.path,
|
||||
partial: exportDoc.outputPath,
|
||||
name: exportDoc.name,
|
||||
type: exportDoc.docType
|
||||
};
|
||||
moduleNavItem.pages.push(exportNavItem);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
docs.push(modulesDoc);
|
||||
|
||||
|
||||
var guidesDoc = {
|
||||
value: { pages: [] },
|
||||
moduleName: 'navigation-guides',
|
||||
serviceName: 'GUIDES',
|
||||
template: 'data-module.template.js',
|
||||
outputPath: 'js/navigation-guides.js'
|
||||
};
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'guide' ) {
|
||||
var guideDoc = {
|
||||
path: doc.path,
|
||||
partial: doc.outputPath,
|
||||
name: doc.name,
|
||||
type: 'guide'
|
||||
};
|
||||
guidesDoc.value.pages.push(guideDoc);
|
||||
}
|
||||
});
|
||||
docs.push(guidesDoc);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,47 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function processClassDocs(log, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['processModuleDocs'],
|
||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||
ignorePrivateMembers: false,
|
||||
$process: function(docs) {
|
||||
var memberDocs = [];
|
||||
var ignorePrivateMembers = this.ignorePrivateMembers;
|
||||
_.forEach(docs, function(classDoc) {
|
||||
if ( classDoc.docType === 'class' ) {
|
||||
|
||||
classDoc.members = [];
|
||||
|
||||
// Create a new doc for each member of the class
|
||||
_.forEach(classDoc.elements, function(memberDoc) {
|
||||
|
||||
if (ignorePrivateMembers && memberDoc.name.literalToken.value.charAt(0) === '_') return;
|
||||
|
||||
classDoc.members.push(memberDoc);
|
||||
memberDocs.push(memberDoc);
|
||||
|
||||
memberDoc.docType = 'member';
|
||||
memberDoc.classDoc = classDoc;
|
||||
memberDoc.name = memberDoc.name.literalToken.value;
|
||||
|
||||
|
||||
if (memberDoc.commentBefore ) {
|
||||
// If this export has a comment, remove it from the list of
|
||||
// comments collected in the module
|
||||
var index = classDoc.moduleDoc.comments.indexOf(memberDoc.commentBefore);
|
||||
if ( index !== -1 ) {
|
||||
classDoc.moduleDoc.comments.splice(index, 1);
|
||||
}
|
||||
|
||||
_.assign(memberDoc, getJSDocComment(memberDoc.commentBefore));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return docs.concat(memberDocs);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,46 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||
$process: function(docs) {
|
||||
var exportDocs = [];
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'module' ) {
|
||||
|
||||
log.debug('processing', doc.moduleTree.moduleName);
|
||||
|
||||
doc.exports = [];
|
||||
|
||||
if ( doc.moduleTree.visit ) {
|
||||
var visitor = new ExportTreeVisitor();
|
||||
visitor.visit(doc.moduleTree);
|
||||
|
||||
_.forEach(visitor.exports, function(exportDoc) {
|
||||
|
||||
doc.exports.push(exportDoc);
|
||||
exportDocs.push(exportDoc);
|
||||
exportDoc.moduleDoc = doc;
|
||||
|
||||
if (exportDoc.comment) {
|
||||
// If this export has a comment, remove it from the list of
|
||||
// comments collected in the module
|
||||
var index = doc.comments.indexOf(exportDoc.comment);
|
||||
if ( index !== -1 ) {
|
||||
doc.comments.splice(index, 1);
|
||||
}
|
||||
|
||||
_.assign(exportDoc, getJSDocComment(exportDoc.comment));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return docs.concat(exportDocs);
|
||||
}
|
||||
};
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
var path = require('canonical-path');
|
||||
|
||||
|
||||
/**
|
||||
* @dgService atScriptFileReader
|
||||
* @description
|
||||
* This file reader will create a simple doc for each
|
||||
* file including a code AST of the AtScript in the file.
|
||||
*/
|
||||
module.exports = function atScriptFileReader(log, atParser, modules) {
|
||||
var reader = {
|
||||
name: 'atScriptFileReader',
|
||||
defaultPattern: /\.js$/,
|
||||
getDocs: function(fileInfo) {
|
||||
|
||||
var moduleDoc = atParser.parseModule(fileInfo);
|
||||
moduleDoc.docType = 'module';
|
||||
moduleDoc.id = moduleDoc.moduleTree.moduleName;
|
||||
moduleDoc.aliases = [moduleDoc.id];
|
||||
|
||||
modules[moduleDoc.id] = moduleDoc;
|
||||
|
||||
// Readers return a collection of docs read from the file
|
||||
// but in this read there is only one document (module) to return
|
||||
return [moduleDoc];
|
||||
}
|
||||
};
|
||||
|
||||
return reader;
|
||||
|
||||
|
||||
};
|
@ -1,55 +0,0 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('atScript file reader', function() {
|
||||
|
||||
var dgeni, injector, reader;
|
||||
|
||||
var fileContent =
|
||||
'import {CONST} from "facade/lang";\n' +
|
||||
'\n' +
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/\n' +
|
||||
'export class Inject {\n' +
|
||||
'token;\n' +
|
||||
'@CONST()\n' +
|
||||
'constructor(token) {\n' +
|
||||
'this.token = token;\n' +
|
||||
'}\n' +
|
||||
'}';
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
reader = injector.get('atScriptFileReader');
|
||||
});
|
||||
|
||||
|
||||
it('should provide a default pattern', function() {
|
||||
expect(reader.defaultPattern).toEqual(/\.js$/);
|
||||
});
|
||||
|
||||
|
||||
it('should parse the file using the atParser and return a single doc', function() {
|
||||
|
||||
var atParser = injector.get('atParser');
|
||||
spyOn(atParser, 'parseModule').and.callThrough();
|
||||
|
||||
var docs = reader.getDocs({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(atParser.parseModule).toHaveBeenCalled();
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].docType).toEqual('module');
|
||||
});
|
||||
|
||||
});
|
@ -1,32 +0,0 @@
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @dgService ngdocFileReader
|
||||
* @description
|
||||
* This file reader will pull the contents from a text file (by default .ngdoc)
|
||||
*
|
||||
* The doc will initially have the form:
|
||||
* ```
|
||||
* {
|
||||
* content: 'the content of the file',
|
||||
* startingLine: 1
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
module.exports = function ngdocFileReader() {
|
||||
var reader = {
|
||||
name: 'ngdocFileReader',
|
||||
defaultPattern: /\.md$/,
|
||||
getDocs: function(fileInfo) {
|
||||
|
||||
// We return a single element array because ngdoc files only contain one document
|
||||
return [{
|
||||
docType: 'guide',
|
||||
content: fileInfo.content,
|
||||
startingLine: 1
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
return reader;
|
||||
};
|
@ -1,45 +0,0 @@
|
||||
var ngdocFileReaderFactory = require('./ngdoc');
|
||||
var path = require('canonical-path');
|
||||
|
||||
describe('ngdocFileReader', function() {
|
||||
|
||||
var fileReader;
|
||||
|
||||
var createFileInfo = function(file, content, basePath) {
|
||||
return {
|
||||
fileReader: fileReader.name,
|
||||
filePath: file,
|
||||
baseName: path.basename(file, path.extname(file)),
|
||||
extension: path.extname(file).replace(/^\./, ''),
|
||||
basePath: basePath,
|
||||
relativePath: path.relative(basePath, file),
|
||||
content: content
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
fileReader = ngdocFileReaderFactory();
|
||||
});
|
||||
|
||||
|
||||
describe('defaultPattern', function() {
|
||||
it('should match .md files', function() {
|
||||
expect(fileReader.defaultPattern.test('abc.md')).toBeTruthy();
|
||||
expect(fileReader.defaultPattern.test('abc.js')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getDocs', function() {
|
||||
it('should return an object containing info about the file and its contents', function() {
|
||||
var fileInfo = createFileInfo('project/path/modules/someModule/foo/docs/subfolder/bar.ngdoc', 'A load of content', 'project/path');
|
||||
expect(fileReader.getDocs(fileInfo)).toEqual([{
|
||||
docType: 'guide',
|
||||
content: 'A load of content',
|
||||
startingLine: 1
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,47 +0,0 @@
|
||||
module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
|
||||
|
||||
function AttachCommentTreeVisitorImpl() {
|
||||
ParseTreeVisitor.call(this);
|
||||
}
|
||||
|
||||
AttachCommentTreeVisitorImpl.prototype = {
|
||||
|
||||
__proto__: ParseTreeVisitor.prototype,
|
||||
|
||||
|
||||
visit: function(tree, comments) {
|
||||
this.comments = comments;
|
||||
this.index = 0;
|
||||
this.currentComment = this.comments[this.index];
|
||||
|
||||
if (this.currentComment) log.silly('comment: ' +
|
||||
this.currentComment.range.start.line + ' - ' +
|
||||
this.currentComment.range.end.line + ' : ' +
|
||||
this.currentComment.range.toString());
|
||||
|
||||
ParseTreeVisitor.prototype.visit.call(this, tree);
|
||||
},
|
||||
|
||||
// Really we ought to subclass ParseTreeVisitor but this is fiddly in ES5 so
|
||||
// it is easier to simply override the prototype's method on the instance
|
||||
visitAny: function(tree) {
|
||||
if (tree && tree.location && tree.location.start && this.currentComment &&
|
||||
this.currentComment.range.end.offset < tree.location.start.offset) {
|
||||
log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
|
||||
while (this.currentComment &&
|
||||
this.currentComment.range.end.offset < tree.location.start.offset) {
|
||||
log.silly('comment: ' + this.currentComment.range.start.line + ' - ' +
|
||||
this.currentComment.range.end.line + ' : ' +
|
||||
this.currentComment.range.toString());
|
||||
tree.commentBefore = this.currentComment;
|
||||
this.currentComment.treeAfter = tree;
|
||||
this.index++;
|
||||
this.currentComment = this.comments[this.index];
|
||||
}
|
||||
}
|
||||
return ParseTreeVisitor.prototype.visitAny.call(this, tree);
|
||||
}
|
||||
};
|
||||
|
||||
return AttachCommentTreeVisitorImpl;
|
||||
};
|
@ -1,103 +0,0 @@
|
||||
module.exports = function ExportTreeVisitor(ParseTreeVisitor, log) {
|
||||
|
||||
function ExportTreeVisitorImpl() {
|
||||
ParseTreeVisitor.call(this);
|
||||
}
|
||||
ExportTreeVisitorImpl.prototype = {
|
||||
|
||||
__proto__: ParseTreeVisitor.prototype,
|
||||
|
||||
visitExportDeclaration: function(tree) {
|
||||
// We are entering an export declaration - create an object to track it
|
||||
this.currentExport = {
|
||||
comment: tree.commentBefore,
|
||||
location: tree.location
|
||||
};
|
||||
log.silly('enter', tree.type, tree.commentBefore ? 'has comment' : '');
|
||||
ParseTreeVisitor.prototype.visitExportDeclaration.call(this, tree);
|
||||
log.silly('exit', this.currentExport);
|
||||
|
||||
if(this.currentExport) {
|
||||
// We are exiting the export declaration - store the export object
|
||||
this.exports.push(this.currentExport);
|
||||
}
|
||||
this.currentExport = null;
|
||||
},
|
||||
|
||||
visitVariableDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.docType = 'var';
|
||||
this.currentExport.name = tree.lvalue.identifierToken.value;
|
||||
this.currentExport.variableDeclaration = tree;
|
||||
}
|
||||
},
|
||||
|
||||
visitFunctionDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = tree.name.identifierToken.value;
|
||||
this.currentExport.functionKind = tree.functionKind;
|
||||
this.currentExport.parameters = tree.parameterList.parameters;
|
||||
this.currentExport.typeAnnotation = tree.typeAnnotation;
|
||||
this.currentExport.annotations = tree.annotations;
|
||||
this.currentExport.docType = 'function';
|
||||
|
||||
log.silly(tree.type, tree.commentBefore ? 'has comment' : '');
|
||||
}
|
||||
},
|
||||
visitClassDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = tree.name.identifierToken.value;
|
||||
this.currentExport.superClass = tree.superClass;
|
||||
this.currentExport.annotations = tree.annotations;
|
||||
this.currentExport.elements = tree.elements;
|
||||
this.currentExport.docType = 'class';
|
||||
}
|
||||
},
|
||||
visitAsyncFunctionDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
}
|
||||
},
|
||||
|
||||
visitExportDefault: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = 'DEFAULT';
|
||||
this.currentExport.defaultExport = tree;
|
||||
// Default exports are either classes, functions or expressions
|
||||
// So we let the super class continue down...
|
||||
ParseTreeVisitor.prototype.visitExportDefault.call(this, tree);
|
||||
}
|
||||
},
|
||||
|
||||
visitNamedExport: function(tree) {
|
||||
this.currentExport = null;
|
||||
// if ( this.currentExport ) {
|
||||
// this.updateExport(tree);
|
||||
|
||||
// this.currentExport.namedExport = tree;
|
||||
// this.currentExport.name = 'NAMED_EXPORT';
|
||||
// // TODO: work out this bit!!
|
||||
// // We need to cope with any export specifiers in the named export
|
||||
// }
|
||||
},
|
||||
|
||||
// TODO - if the export is an expression, find the thing that is being
|
||||
// exported and use it and its comments for docs
|
||||
|
||||
updateExport: function(tree) {
|
||||
this.currentExport.comment = this.currentExport.comment || tree.commentBefore;
|
||||
this.currentExport.docType = tree.type;
|
||||
},
|
||||
|
||||
visit: function(tree) {
|
||||
this.exports = [];
|
||||
ParseTreeVisitor.prototype.visit.call(this, tree);
|
||||
}
|
||||
};
|
||||
|
||||
return ExportTreeVisitorImpl;
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
var traceur = require('traceur/src/node/traceur.js');
|
||||
|
||||
module.exports = function ParseTreeVisitor() {
|
||||
console.log(System.map.traceur);
|
||||
return System.get(System.map.traceur + '/src/syntax/ParseTreeVisitor.js').ParseTreeVisitor;
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
var traceur = require('traceur/src/node/traceur.js');
|
||||
|
||||
module.exports = function SourceFile() {
|
||||
return System.get(System.map.traceur + '/src/syntax/SourceFile.js').SourceFile;
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
module.exports = function TraceurParser() {
|
||||
return System.get('transpiler/src/parser').Parser;
|
||||
};
|
@ -1,74 +0,0 @@
|
||||
var file2modulename = require('../../../tools/build/file2modulename');
|
||||
/**
|
||||
* Wrapper around traceur that can parse the contents of a file
|
||||
*/
|
||||
module.exports = function atParser(AttachCommentTreeVisitor, SourceFile, TraceurParser, traceurOptions, log) {
|
||||
|
||||
var service = {
|
||||
/**
|
||||
* The options to pass to traceur
|
||||
*/
|
||||
traceurOptions: {
|
||||
annotations: true, // parse annotations
|
||||
types: true, // parse types
|
||||
memberVariables: true, // parse class fields
|
||||
commentCallback: true // handle comments
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a module AST from the contents of a file.
|
||||
* @param {Object} fileInfo information about the file to parse
|
||||
* @return { { moduleTree: Object, comments: Array } } An object containing the parsed module
|
||||
* AST and an array of all the comments found in the file
|
||||
*/
|
||||
parseModule: parseModule
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
|
||||
// Parse the contents of the file using traceur
|
||||
function parseModule(fileInfo) {
|
||||
|
||||
var moduleName = file2modulename(fileInfo.relativePath);
|
||||
var sourceFile = new SourceFile(moduleName, fileInfo.content);
|
||||
var comments = [];
|
||||
var moduleTree;
|
||||
var parser = new TraceurParser(sourceFile);
|
||||
|
||||
// Configure the parser
|
||||
parser.handleComment = function(range) {
|
||||
comments.push({ range: range });
|
||||
};
|
||||
traceurOptions.setFromObject(service.traceurOptions);
|
||||
|
||||
try {
|
||||
// Parse the file as a module, attaching the comments
|
||||
moduleTree = parser.parseModule();
|
||||
attachComments(moduleTree, comments);
|
||||
} catch(ex) {
|
||||
// HACK: sometime traceur crashes for various reasons including
|
||||
// Not Yet Implemented (NYI)!
|
||||
log.error(ex.stack);
|
||||
moduleTree = {};
|
||||
}
|
||||
log.debug(moduleName);
|
||||
moduleTree.moduleName = moduleName;
|
||||
|
||||
// We return the module AST but also a collection of all the comments
|
||||
// since it can be helpful to iterate through them without having to
|
||||
// traverse the AST again
|
||||
return {
|
||||
moduleTree: moduleTree,
|
||||
comments: comments
|
||||
};
|
||||
}
|
||||
|
||||
// attach the comments to their nearest code tree
|
||||
function attachComments(tree, comments) {
|
||||
|
||||
var visitor = new AttachCommentTreeVisitor();
|
||||
// Visit every node of the tree using our custom method
|
||||
visitor.visit(tree, comments);
|
||||
}
|
||||
};
|
@ -1,80 +0,0 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('atParser service', function() {
|
||||
|
||||
var dgeni, injector, parser;
|
||||
|
||||
var fileContent =
|
||||
'import {CONST} from "facade/lang";\n' +
|
||||
'\n' +
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/\n' +
|
||||
'export class Inject {\n' +
|
||||
'token;\n' +
|
||||
'@CONST()\n' +
|
||||
'constructor({a,b}:{a:string, b:string}) {\n' +
|
||||
'this.token = a;\n' +
|
||||
'}\n' +
|
||||
'}';
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
parser = injector.get('atParser');
|
||||
});
|
||||
|
||||
it('should extract the comments from the file', function() {
|
||||
var result = parser.parseModule({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(result.comments[0].range.toString()).toEqual(
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/'
|
||||
);
|
||||
});
|
||||
|
||||
it('should extract a module AST from the file', function() {
|
||||
var result = parser.parseModule({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(result.moduleTree.moduleName).toEqual('di/src/annotations');
|
||||
expect(result.moduleTree.scriptItemList[0].type).toEqual('IMPORT_DECLARATION');
|
||||
|
||||
expect(result.moduleTree.scriptItemList[1].type).toEqual('EXPORT_DECLARATION');
|
||||
});
|
||||
|
||||
it('should attach comments to their following AST', function() {
|
||||
var result = parser.parseModule({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(result.moduleTree.scriptItemList[1].commentBefore.range.toString()).toEqual(
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/'
|
||||
);
|
||||
});
|
||||
});
|
@ -1,28 +0,0 @@
|
||||
var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
|
||||
|
||||
/**
|
||||
* Extact comment info from a comment object
|
||||
* @param {Object} comment object to process
|
||||
* @return { {startingLine, endingLine, content, codeTree}= } a comment info object
|
||||
* or undefined if the comment is not a jsdoc style comment
|
||||
*/
|
||||
module.exports = function getJSDocComment() {
|
||||
return function(comment) {
|
||||
|
||||
var commentInfo;
|
||||
|
||||
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
|
||||
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
|
||||
commentBody = commentBody.replace(LEADING_STAR, '').trim();
|
||||
|
||||
commentInfo = {
|
||||
startingLine: comment.range.start.line,
|
||||
endingLine: comment.range.end.line,
|
||||
content: commentBody,
|
||||
codeTree: comment.treeAfter
|
||||
};
|
||||
});
|
||||
|
||||
return commentInfo;
|
||||
};
|
||||
};
|
@ -1,67 +0,0 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('getJSDocComment service', function() {
|
||||
|
||||
var dgeni, injector, getJSDocComment;
|
||||
|
||||
function createComment(commentString, start, end, codeTree) {
|
||||
return {
|
||||
range: {
|
||||
toString: function() { return commentString; },
|
||||
start: { line: start },
|
||||
end: { line: end },
|
||||
},
|
||||
treeAfter: codeTree
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
getJSDocComment = injector.get('getJSDocComment');
|
||||
});
|
||||
|
||||
it('should only return an object if the comment starts with /** and ends with */', function() {
|
||||
var result = getJSDocComment(createComment('/** this is a jsdoc comment */'));
|
||||
expect(result).toBeDefined();
|
||||
|
||||
result = getJSDocComment(createComment('/* this is a normal comment */'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('this is not a valid comment */'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('nor is this'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('/* or even this'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('/** and this'));
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should return a result that contains info about the comment', function() {
|
||||
var codeTree = {};
|
||||
var result = getJSDocComment(createComment('/** this is a comment */', 10, 20, codeTree));
|
||||
expect(result.startingLine).toEqual(10);
|
||||
expect(result.endingLine).toEqual(20);
|
||||
expect(result.codeTree).toBe(codeTree);
|
||||
});
|
||||
|
||||
it('should strip off leading stars from each line', function() {
|
||||
var result = getJSDocComment(createComment(
|
||||
'/** this is a jsdoc comment */\n' +
|
||||
' *\n' +
|
||||
' * some content\n' +
|
||||
' */'
|
||||
));
|
||||
expect(result.content).toEqual(
|
||||
'this is a jsdoc comment */\n' +
|
||||
'\n' +
|
||||
'some content'
|
||||
);
|
||||
});
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
module.exports = function traceurOptions() {
|
||||
return System.get(System.map.traceur + '/src/Options.js').options;
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{$ doc.name $} <span class="type">class</span></h1>
|
||||
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
<h2>Members</h2>
|
||||
{% for member in doc.members %}
|
||||
<h3>{$ member.name $}</h3>
|
||||
<p>{$ member.description | marked $}</p>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
@ -1,9 +0,0 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{$ doc.id $}</h1>
|
||||
<h2>({$ doc.docType $})</h2>
|
||||
<div>
|
||||
{$ doc.description | marked $}
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,3 +0,0 @@
|
||||
angular.module('{$ doc.moduleName $}', [])
|
||||
|
||||
.value('{$ doc.serviceName $}', {$ doc.value | json $});
|
@ -1,5 +0,0 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
{$ doc.description | marked $}
|
||||
{% endblock %}
|
@ -1 +0,0 @@
|
||||
{% block body %}{% endblock %}
|
@ -1,16 +0,0 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1 class="id">{$ doc.id $} <span class="type">module</span></h1>
|
||||
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
{% if doc.exports.length %}
|
||||
<h2>Exports</h2>
|
||||
<ul>
|
||||
{%- for exportDoc in doc.exports %}
|
||||
<li><a href="/{$ exportDoc.path $}">{$ exportDoc.name $} {$ exportDoc.docType $}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -1,21 +0,0 @@
|
||||
var Package = require('dgeni').Package;
|
||||
var basePackage = require('../dgeni-package');
|
||||
|
||||
|
||||
module.exports = new Package('angular-public', [basePackage])
|
||||
|
||||
.processor(require('./processors/filterPublicDocs'))
|
||||
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push({ name: 'publicModule' });
|
||||
})
|
||||
|
||||
.config(function(processClassDocs, filterPublicDocs, EXPORT_DOC_TYPES) {
|
||||
processClassDocs.ignorePrivateMembers = true;
|
||||
filterPublicDocs.docTypes = EXPORT_DOC_TYPES;
|
||||
})
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/public_docs';
|
||||
});
|
@ -1,51 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function filterPublicDocs(modules) {
|
||||
return {
|
||||
$runAfter: ['tags-parsed'],
|
||||
$runBefore: ['computing-ids'],
|
||||
docTypes: [],
|
||||
$validate: {
|
||||
docTypes: { presence: true }
|
||||
},
|
||||
$process: function(docs) {
|
||||
|
||||
docTypes = this.docTypes;
|
||||
|
||||
|
||||
docs = _.filter(docs, function(doc) {
|
||||
|
||||
if (docTypes.indexOf(doc.docType) === -1) return true;
|
||||
if (!doc.publicModule) return false;
|
||||
|
||||
updateModule(doc);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
docs = _.filter(docs, function(doc) {
|
||||
return doc.docType !== 'module' || doc.isPublic;
|
||||
});
|
||||
return docs;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function updateModule(classDoc) {
|
||||
|
||||
var originalModule = classDoc.moduleDoc;
|
||||
var publicModule = modules[classDoc.publicModule];
|
||||
|
||||
if (!publicModule) {
|
||||
throw new Error('Missing module definition: "' + classDoc.publicModule + '"\n' +
|
||||
'Referenced in class: "' + classDoc.moduleDoc.id + '/' + classDoc.name + '"');
|
||||
}
|
||||
|
||||
publicModule.isPublic = true;
|
||||
|
||||
_.remove(classDoc.moduleDoc.exports, function(doc) { return doc === classDoc; });
|
||||
classDoc.moduleDoc = publicModule;
|
||||
publicModule.exports.push(classDoc);
|
||||
|
||||
}
|
||||
};
|
121
docs/typescript-definition-package/index.js
Normal file
121
docs/typescript-definition-package/index.js
Normal file
@ -0,0 +1,121 @@
|
||||
var Package = require('dgeni').Package;
|
||||
var jsdocPackage = require('dgeni-packages/jsdoc');
|
||||
var nunjucksPackage = require('dgeni-packages/nunjucks');
|
||||
var typescriptPackage = require('../typescript-package');
|
||||
var gitPackage = require('dgeni-packages/git');
|
||||
var path = require('canonical-path');
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('angular-v2-docs', [jsdocPackage, nunjucksPackage, typescriptPackage, gitPackage])
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/createTypeDefinitionFile'))
|
||||
|
||||
.config(function(readFilesProcessor, inlineTagProcessor) {
|
||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||
// Don't run unwanted processors
|
||||
readFilesProcessor.$enabled = false;
|
||||
inlineTagProcessor.$enabled = false;
|
||||
})
|
||||
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'info';
|
||||
})
|
||||
|
||||
|
||||
.config(function(renderDocsProcessor, versionInfo) {
|
||||
renderDocsProcessor.extraData.versionInfo = versionInfo;
|
||||
})
|
||||
|
||||
.config(function(readFilesProcessor, inlineTagProcessor, readTypeScriptModules, createTypeDefinitionFile) {
|
||||
|
||||
// Don't run unwanted processors
|
||||
readFilesProcessor.$enabled = false; // We are not using the normal file reading processor
|
||||
inlineTagProcessor.$enabled = false; // We are not actually processing the inline link tags
|
||||
|
||||
// Configure file reading
|
||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||
readTypeScriptModules.sourceFiles = [
|
||||
'angular2/angular2.ts',
|
||||
'angular2/web_worker/worker.ts',
|
||||
'angular2/web_worker/ui.ts',
|
||||
'angular2/router.ts',
|
||||
'angular2/http.ts',
|
||||
'angular2/testing.ts'
|
||||
];
|
||||
readTypeScriptModules.basePath = path.resolve(path.resolve(__dirname, '../../modules'));
|
||||
|
||||
createTypeDefinitionFile.typeDefinitions = [
|
||||
{
|
||||
id: 'angular2/angular2',
|
||||
references: [],
|
||||
modules: {
|
||||
'angular2/angular2': {namespace: 'ng', id: 'angular2/angular2'},
|
||||
'angular2/web_worker/worker': {namespace: 'ngWorker', id: 'angular2/web_worker/worker'},
|
||||
'angular2/web_worker/ui': {namespace: 'ngUi', id: 'angular2/web_worker/ui'}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'angular2/router',
|
||||
references: ['./angular2.d.ts'],
|
||||
remapTypes: {Type: 'ng.Type', InjectableReference: 'ng.InjectableReference', ElementRef: 'ng.ElementRef', DynamicComponentLoader: 'ng.DynamicComponentLoader'},
|
||||
modules: {'angular2/router': {namespace: 'ngRouter', id: 'angular2/router'}}
|
||||
},
|
||||
{
|
||||
id: 'angular2/http',
|
||||
references: ['./angular2.d.ts'],
|
||||
remapTypes: {Type: 'ng.Type', Observable: 'ng.Observable', EventEmitter: 'ng.EventEmitter', InjectableReference: 'ng.InjectableReference' },
|
||||
modules: {'angular2/http': {namespace: 'ngHttp', id: 'angular2/http'}}
|
||||
},
|
||||
{
|
||||
id: 'angular2/testing',
|
||||
references: ['./angular2.d.ts'],
|
||||
remapTypes: { Type: 'ng.Type', Binding: 'ng.Binding', Provider: 'ng.Provider', ViewMetadata: 'ng.ViewMetadata', Injector: 'ng.Injector',
|
||||
Predicate: 'ng.Predicate', ElementRef: 'ng.ElementRef', DebugElement: 'ng.DebugElement',
|
||||
InjectableReference: 'ng.InjectableReference', ComponentRef: 'ng.ComponentRef' },
|
||||
modules: {'angular2/testing': {namespace: 'ngTesting', id: 'angular2/testing'}}
|
||||
}
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
.config(function(parseTagsProcessor, getInjectables) {
|
||||
// We actually don't want to parse param docs in this package as we are getting the data out using TS
|
||||
parseTagsProcessor.tagDefinitions.forEach(function(tagDef) {
|
||||
if (tagDef.name === 'param') {
|
||||
tagDef.docProperty = 'paramData';
|
||||
tagDef.transforms = [];
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/docs';
|
||||
})
|
||||
|
||||
|
||||
// Configure rendering
|
||||
.config(function(templateFinder, templateEngine) {
|
||||
|
||||
// Nunjucks and Angular conflict in their template bindings so change Nunjucks
|
||||
templateEngine.config.tags = {
|
||||
variableStart: '{$',
|
||||
variableEnd: '$}'
|
||||
};
|
||||
|
||||
templateFinder.templateFolders
|
||||
.unshift(path.resolve(__dirname, 'templates'));
|
||||
|
||||
templateFinder.templatePatterns = [
|
||||
'${ doc.template }',
|
||||
'${ doc.id }.${ doc.docType }.template.html',
|
||||
'${ doc.id }.template.html',
|
||||
'${ doc.docType }.template.html',
|
||||
'common.template.html'
|
||||
];
|
||||
});
|
11
docs/typescript-definition-package/mocks/mockPackage.js
Normal file
11
docs/typescript-definition-package/mocks/mockPackage.js
Normal file
@ -0,0 +1,11 @@
|
||||
var Package = require('dgeni').Package;
|
||||
|
||||
module.exports = function mockPackage() {
|
||||
|
||||
return new Package('mockPackage', [require('../')])
|
||||
|
||||
// provide a mock log service
|
||||
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
|
||||
// .factory('templateEngine', function() { return {}; });
|
||||
|
||||
};
|
220
docs/typescript-definition-package/processors/code_gen.js
Normal file
220
docs/typescript-definition-package/processors/code_gen.js
Normal file
@ -0,0 +1,220 @@
|
||||
'use strict';
|
||||
|
||||
function DtsSerializer(remap) {
|
||||
this.remap = remap;
|
||||
}
|
||||
|
||||
DtsSerializer.prototype = {
|
||||
_initializerRegex: /\s*=[^>][^,}]*/g,
|
||||
|
||||
constructor: DtsSerializer,
|
||||
|
||||
declaration: function(buffer, ast) {
|
||||
buffer.push(ast.name);
|
||||
if (ast.optional) buffer.push('?');
|
||||
if (ast.typeParameters) {
|
||||
buffer.push('<');
|
||||
buffer.push(ast.typeParameters.join(', '));
|
||||
buffer.push('>');
|
||||
}
|
||||
if (ast.parameters) {
|
||||
buffer.push('(');
|
||||
var parameters = ast.parameters;
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
parameters[i] = parameters[i].replace(this._initializerRegex, '');
|
||||
}
|
||||
buffer.push(parameters.join(', '));
|
||||
buffer.push(')');
|
||||
}
|
||||
if (!isConstructor(ast)) {
|
||||
if (ast.returnType) {
|
||||
buffer.push(': ', ast.returnType);
|
||||
} else if (ast.parameters) {
|
||||
buffer.push(': void');
|
||||
} else {
|
||||
buffer.push(': any');
|
||||
}
|
||||
}
|
||||
buffer.push(';\n');
|
||||
},
|
||||
|
||||
comment: function(buffer, commentText) {
|
||||
if (!(commentText && commentText.match(/\S/))) return;
|
||||
|
||||
buffer.push('/**\n');
|
||||
commentText.replace(/\n*$/, '').split('\n').forEach(function(line) {
|
||||
buffer.push(' * ' + line + '\n');
|
||||
});
|
||||
buffer.push(' */\n');
|
||||
},
|
||||
|
||||
member: function(buffer, ast) {
|
||||
if (ast.private || ast.internal) return;
|
||||
|
||||
buffer.push('\n');
|
||||
this.comment(buffer, ast.content);
|
||||
|
||||
if (ast.isStatic) buffer.push('static ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
interfaceOrClass: function(buffer, ast, isInterface) {
|
||||
if (ast.abstract) {
|
||||
buffer.push('abstract ');
|
||||
}
|
||||
|
||||
buffer.push(isInterface ? 'interface ' : 'class ');
|
||||
buffer.push(ast.name);
|
||||
buffer.push(ast.typeParams);
|
||||
buffer.push(ast.heritage);
|
||||
buffer.push(' {');
|
||||
buffer.indent();
|
||||
if (ast.newMember) this.member(buffer, ast.newMember);
|
||||
if (ast.callMember) this.member(buffer, ast.callMember);
|
||||
if (ast.constructorDoc) this.member(buffer, ast.constructorDoc);
|
||||
|
||||
ast.statics.forEach(function(staticMember) {
|
||||
this.member(buffer, staticMember);
|
||||
}.bind(this));
|
||||
|
||||
ast.members.forEach(function(member) {
|
||||
this.member(buffer, member);
|
||||
}.bind(this));
|
||||
|
||||
buffer.unindent();
|
||||
buffer.push('}');
|
||||
},
|
||||
|
||||
enum: function(buffer, ast) {
|
||||
buffer.push('enum ');
|
||||
buffer.push(ast.name);
|
||||
buffer.push(ast.typeParams);
|
||||
buffer.push(ast.heritage);
|
||||
buffer.push(' {');
|
||||
buffer.indent();
|
||||
|
||||
ast.members.forEach(function(member, index) {
|
||||
buffer.push('\n');
|
||||
this.comment(buffer, member.content);
|
||||
buffer.push(member.name);
|
||||
if (index !== (ast.members.length - 1)) {
|
||||
buffer.push(',\n');
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
buffer.unindent();
|
||||
buffer.push('}\n');
|
||||
},
|
||||
|
||||
function: function(buffer, ast) {
|
||||
buffer.push('function ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
var: function(buffer, ast) {
|
||||
buffer.push('var ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
let: function(buffer, ast) {
|
||||
buffer.push('let ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
const: function(buffer, ast) {
|
||||
buffer.push('const ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
typeAlias: function(buffer, ast) {
|
||||
buffer.push('type ', ast.name, ' = ', ast.returnType);
|
||||
},
|
||||
|
||||
serializeExport: function(ast) {
|
||||
var buffer = new Buffer();
|
||||
buffer.push('\n');
|
||||
|
||||
try {
|
||||
this.comment(buffer, ast.content);
|
||||
|
||||
switch (ast.docType) {
|
||||
case 'class': this.interfaceOrClass(buffer, ast, false); break;
|
||||
case 'interface': this.interfaceOrClass(buffer, ast, true); break;
|
||||
case 'function': this.function(buffer, ast); break;
|
||||
case 'enum': this.enum(buffer, ast); break;
|
||||
case 'var': this.var(buffer, ast); break;
|
||||
case 'let': this.let(buffer, ast); break;
|
||||
case 'const': this.const(buffer, ast); break;
|
||||
case 'type-alias': this.typeAlias(buffer, ast); break;
|
||||
default: throw new Error("unknown docType: " + ast.docType);
|
||||
}
|
||||
|
||||
var string = buffer.toString();
|
||||
for (var key in this.remap) {
|
||||
if (this.remap.hasOwnProperty(key)) {
|
||||
string = string.replace(new RegExp('\\b' + key + '\\b', 'gm'), this.remap[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
} catch (e) {
|
||||
console.log(e.toString(), e.stack);
|
||||
return 'ERROR: ' + e.toString();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function Buffer() {
|
||||
this._globalBuffer = [];
|
||||
this._indentedBuffer = [];
|
||||
this._indentationLevel = 1;
|
||||
}
|
||||
|
||||
Buffer.prototype = {
|
||||
constructor: Buffer,
|
||||
|
||||
push: function() {
|
||||
this._indentedBuffer.push.apply(this._indentedBuffer, arguments);
|
||||
},
|
||||
|
||||
indent: function() {
|
||||
this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')});
|
||||
this._indentationLevel++;
|
||||
this._indentedBuffer = [];
|
||||
},
|
||||
|
||||
unindent: function() {
|
||||
this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')});
|
||||
this._indentationLevel--;
|
||||
this._indentedBuffer = [];
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
if (this._indentationLevel !== 1) {
|
||||
throw new Exception("Forgot to unindent? Indentation level: " + this._indentationLevel);
|
||||
}
|
||||
|
||||
this.unindent();
|
||||
|
||||
var string = '';
|
||||
|
||||
this._globalBuffer.forEach(function(indentedChunk) {
|
||||
var indentation = (new Array(indentedChunk.indentationLevel * 2 + 1)).join(' ');
|
||||
indentedChunk.content.split('\n').forEach(function(line) {
|
||||
string += indentation + line + '\n';
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
function isConstructor(ast) {
|
||||
return ast.parameters && ast.name === "constructor";
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DtsSerializer: DtsSerializer
|
||||
};
|
||||
|
@ -0,0 +1,72 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
var codeGen = require('./code_gen.js');
|
||||
|
||||
module.exports = function createTypeDefinitionFile(log, convertPrivateClassesToInterfaces) {
|
||||
|
||||
return {
|
||||
$runAfter: ['processing-docs'],
|
||||
$runBefore: ['docs-processed'],
|
||||
$validate: {
|
||||
dtsPath: { presence: true },
|
||||
dtsExtension: { presence: true },
|
||||
typeDefinitions: { presence: true }
|
||||
},
|
||||
dtsPath: 'typings',
|
||||
dtsExtension: '.d.ts',
|
||||
typeDefinitions: [],
|
||||
$process: function(docs) {
|
||||
var dtsPath = this.dtsPath;
|
||||
var dtsExtension = this.dtsExtension;
|
||||
|
||||
// For each type definition that we wish to create we define a dgeni "doc" for it
|
||||
var typeDefDocs = _.map(this.typeDefinitions, function(def) {
|
||||
|
||||
var id = def.id + dtsExtension;
|
||||
var docPath = path.join(dtsPath, id);
|
||||
|
||||
return {
|
||||
docType: 'type-definition',
|
||||
id: id,
|
||||
aliases: [id],
|
||||
path: docPath,
|
||||
outputPath: docPath,
|
||||
// A type definition may include a number of top level modules
|
||||
// And those modules could be aliased (such as 'angular2/angular2.api' ->
|
||||
// 'angular2/angular2')
|
||||
moduleDocs: _.transform(def.modules,
|
||||
function(moduleDocs, props, alias) {
|
||||
moduleDocs[props.id] = {
|
||||
id: alias,
|
||||
doc: null, namespace: props.namespace,
|
||||
references: def.references
|
||||
};
|
||||
}),
|
||||
dts: new codeGen.DtsSerializer(def.remapTypes)
|
||||
};
|
||||
});
|
||||
|
||||
// Now add all the module docs to their corresponding type definition doc
|
||||
_.forEach(docs, function(doc) {
|
||||
_.forEach(typeDefDocs, function(typeDefDoc) {
|
||||
if(typeDefDoc.moduleDocs[doc.id]) {
|
||||
// Add a copy, because we are going to modify it
|
||||
typeDefDoc.moduleDocs[doc.id].doc = doc;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return _.filter(typeDefDocs, function(doc) {
|
||||
_.forEach(doc.moduleDocs, function(modDoc, alias) {
|
||||
if (!doc || !modDoc.doc) {
|
||||
log.error('createTypeDefinitionFile processor: no such module "' + alias + '" (Did you forget to add it to the modules to load?)');
|
||||
doc = null;
|
||||
return;
|
||||
}
|
||||
convertPrivateClassesToInterfaces(modDoc.doc.exports, true);
|
||||
});
|
||||
return !!doc;
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('createTypeDefinitionFile processor', function() {
|
||||
var dgeni, injector, processor;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
processor = injector.get('createTypeDefinitionFile');
|
||||
|
||||
// Initialize the processor
|
||||
processor.typeDefinitions = [{
|
||||
id: 'angular2/angular2',
|
||||
modules: {
|
||||
'angular2/angular2': {
|
||||
id: 'angular2/angular2',
|
||||
namespace: 'ng'
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('classes with @internal constructors', function() {
|
||||
|
||||
it('should convert heritage from `implements` into `extends`', function() {
|
||||
|
||||
// Create some mock docs for testing
|
||||
var docs = [
|
||||
{
|
||||
id: 'angular2/angular2',
|
||||
exports: [
|
||||
{ docType: 'class', heritage: 'implements Xyz', constructorDoc: { internal: true } }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
docs = processor.$process(docs);
|
||||
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].docType).toEqual('type-definition');
|
||||
|
||||
var moduleDoc = docs[0].moduleDocs['angular2/angular2'].doc;
|
||||
expect(moduleDoc.exports.length).toEqual(2);
|
||||
expect(moduleDoc.exports[0].heritage).toEqual('extends Xyz');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
{% extends '../type-definition.template.html' %}
|
||||
{% block staticDeclarations %}
|
||||
|
||||
interface Map<K,V> {}
|
||||
|
||||
{% for alias, module in doc.moduleDocs %}
|
||||
declare module {$ module.namespace $} {
|
||||
// See https://github.com/Microsoft/TypeScript/issues/1168
|
||||
class BaseException /* extends Error */ {
|
||||
message: string;
|
||||
stack: string;
|
||||
toString(): string;
|
||||
}
|
||||
interface InjectableReference {}
|
||||
}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
@ -0,0 +1,43 @@
|
||||
|
||||
{%- macro commentBlock(doc, level) -%}
|
||||
{%- if doc.content | trim %}
|
||||
|
||||
{% if level > 1 %}{$ '/**' | indent(level-1, true) | replace(r/\n$/, "") $}{% else %}/**{% endif %}
|
||||
{$ doc.content | trim | replace(r/^/gm, "* ") | indent(level, true) | replace(r/\n$/, "") $}
|
||||
{$ '*/' | indent(level, true) | replace(r/\n$/, "") $}{% endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
|
||||
// Type definitions for Angular v{$ versionInfo.currentVersion.full | replace(r/\+/, "_") $}
|
||||
// Project: http://angular.io/
|
||||
// Definitions by: angular team <https://github.com/angular/>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
// ***********************************************************
|
||||
// This file is generated by the Angular build process.
|
||||
// Please do not create manual edits or send pull requests
|
||||
// modifying this file.
|
||||
// ***********************************************************
|
||||
{% for alias, module in doc.moduleDocs %}
|
||||
{%- if module.references.length %}
|
||||
// {$ alias $} depends transitively on these libraries.
|
||||
// If you don't have them installed you can install them using TSD
|
||||
// https://github.com/DefinitelyTyped/tsd
|
||||
{%- endif %}
|
||||
{% for reference in module.references %}
|
||||
///<reference path="{$ reference $}"/>{% endfor %}{% endfor %}
|
||||
{% block staticDeclarations %}{% endblock %}
|
||||
{% for alias, module in doc.moduleDocs %}
|
||||
{$ commentBlock(module.doc, 1) $}
|
||||
declare module {$ module.namespace $} {
|
||||
|
||||
{%- for export in module.doc.exports -%}
|
||||
{$ doc.dts.serializeExport(export) $}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
declare module "{$ alias $}" {
|
||||
export = {$ module.namespace $};
|
||||
}
|
||||
|
||||
{% endfor %}
|
74
docs/typescript-package/index.js
Normal file
74
docs/typescript-package/index.js
Normal file
@ -0,0 +1,74 @@
|
||||
var basePackage = require('dgeni-packages/base');
|
||||
var jsdocPackage = require('dgeni-packages/jsdoc');
|
||||
var Package = require('dgeni').Package;
|
||||
var path = require('canonical-path');
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('typescript-parsing', [basePackage, jsdocPackage])
|
||||
|
||||
// Register the services and file readers
|
||||
.factory(require('./services/modules'))
|
||||
.factory(require('./services/tsParser'))
|
||||
.factory(require('./services/tsParser/createCompilerHost'))
|
||||
.factory(require('./services/tsParser/getFileInfo'))
|
||||
.factory(require('./services/tsParser/getExportDocType'))
|
||||
.factory(require('./services/tsParser/getContent'))
|
||||
|
||||
.factory(require('./services/convertPrivateClassesToInterfaces'))
|
||||
|
||||
.factory('EXPORT_DOC_TYPES', function() {
|
||||
return [
|
||||
'class',
|
||||
'interface',
|
||||
'function',
|
||||
'var',
|
||||
'const',
|
||||
'enum',
|
||||
'type-alias'
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/readTypeScriptModules'))
|
||||
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'warn';
|
||||
})
|
||||
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push({ name: 'internal', transforms: function() { return true; } });
|
||||
})
|
||||
|
||||
// Configure ids and paths
|
||||
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['member'],
|
||||
idTemplate: '${classDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['member'],
|
||||
pathTemplate: '${classDoc.path}/${name}',
|
||||
getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc
|
||||
});
|
||||
|
||||
var MODULES_DOCS_PATH = 'partials/modules';
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['module'],
|
||||
pathTemplate: '/${id}',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
pathTemplate: '${moduleDoc.path}/${name}',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${path}/index.html'
|
||||
});
|
||||
|
||||
});
|
@ -5,6 +5,7 @@ module.exports = function mockPackage() {
|
||||
return new Package('mockPackage', [require('../')])
|
||||
|
||||
// provide a mock log service
|
||||
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); });
|
||||
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
|
||||
.factory('templateEngine', function() { return {}; });
|
||||
|
||||
};
|
@ -0,0 +1,4 @@
|
||||
export var __esModule = true;
|
||||
export class OKToExport {}
|
||||
export function _thisIsPrivate() {}
|
||||
export var thisIsOK = '!';
|
@ -0,0 +1,5 @@
|
||||
export interface MyInterface {
|
||||
optionalProperty? : string
|
||||
<T, U extends Findable<T>>(param: T) : U
|
||||
new (param: number) : MyInterface
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export class Test {
|
||||
firstItem;
|
||||
constructor() { this.doStuff(); }
|
||||
otherMethod() {}
|
||||
doStuff() {}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export var x = 10;
|
@ -0,0 +1 @@
|
||||
export { x as y} from './privateModule';
|
1
docs/typescript-package/mocks/tsParser/importedSrc.ts
Normal file
1
docs/typescript-package/mocks/tsParser/importedSrc.ts
Normal file
@ -0,0 +1 @@
|
||||
export var x = 100;
|
34
docs/typescript-package/mocks/tsParser/testSrc.ts
Normal file
34
docs/typescript-package/mocks/tsParser/testSrc.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This is the module description
|
||||
*/
|
||||
|
||||
export * from 'importedSrc';
|
||||
|
||||
/**
|
||||
* This is some random other comment
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is MyClass
|
||||
*/
|
||||
export class MyClass {
|
||||
message: String;
|
||||
|
||||
/**
|
||||
* Create a new MyClass
|
||||
* @param {String} name The name to say hello to
|
||||
*/
|
||||
constructor(name) { this.message = 'hello ' + name; }
|
||||
|
||||
/**
|
||||
* Return a greeting message
|
||||
*/
|
||||
greet() { return this.message; }
|
||||
}
|
||||
|
||||
/**
|
||||
* An exported function
|
||||
*/
|
||||
export var myFn = (val: number) => return val * 2;
|
413
docs/typescript-package/processors/readTypeScriptModules.js
Normal file
413
docs/typescript-package/processors/readTypeScriptModules.js
Normal file
@ -0,0 +1,413 @@
|
||||
var glob = require('glob');
|
||||
var path = require('canonical-path');
|
||||
var _ = require('lodash');
|
||||
var ts = require('typescript');
|
||||
|
||||
module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
|
||||
getExportDocType, getContent, log) {
|
||||
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
|
||||
$validate: {
|
||||
sourceFiles: {presence: true},
|
||||
basePath: {presence: true},
|
||||
hidePrivateMembers: {inclusion: [true, false]},
|
||||
sortClassMembers: {inclusion: [true, false]},
|
||||
ignoreExportsMatching: {}
|
||||
},
|
||||
|
||||
// A collection of globs that identify those modules for which we should create docs
|
||||
sourceFiles: [],
|
||||
// The base path from which to load the source files
|
||||
basePath: '.',
|
||||
// We can ignore members of classes that are private
|
||||
hidePrivateMembers: true,
|
||||
// We leave class members sorted in order of declaration
|
||||
sortClassMembers: false,
|
||||
// We can provide a collection of strings or regexes to ignore exports whose export names match
|
||||
ignoreExportsMatching: ['___esModule'],
|
||||
|
||||
$process: function(docs) {
|
||||
|
||||
// Convert ignoreExportsMatching to an array of regexes
|
||||
var ignoreExportsMatching = convertToRegexCollection(this.ignoreExportsMatching);
|
||||
|
||||
var hidePrivateMembers = this.hidePrivateMembers;
|
||||
var sortClassMembers = this.sortClassMembers;
|
||||
|
||||
var basePath = path.resolve(this.basePath);
|
||||
var filesPaths = expandSourceFiles(this.sourceFiles, basePath);
|
||||
var parseInfo = tsParser.parse(filesPaths, this.basePath);
|
||||
var moduleSymbols = parseInfo.moduleSymbols;
|
||||
|
||||
// Iterate through each of the modules that were parsed and generate a module doc
|
||||
// as well as docs for each module's exports.
|
||||
moduleSymbols.forEach(function(moduleSymbol) {
|
||||
|
||||
var moduleDoc = createModuleDoc(moduleSymbol, basePath);
|
||||
|
||||
// Add this module doc to the module lookup collection and the docs collection
|
||||
modules[moduleDoc.id] = moduleDoc;
|
||||
docs.push(moduleDoc);
|
||||
|
||||
// Iterate through this module's exports and generate a doc for each
|
||||
moduleSymbol.exportArray.forEach(function(exportSymbol) {
|
||||
|
||||
// Ignore exports starting with an underscore
|
||||
if (anyMatches(ignoreExportsMatching, exportSymbol.name)) return;
|
||||
|
||||
// If the symbol is an Alias then for most things we want the original resolved symbol
|
||||
var resolvedExport = exportSymbol.resolvedSymbol || exportSymbol;
|
||||
var exportDoc = createExportDoc(exportSymbol.name, resolvedExport, moduleDoc, basePath, parseInfo.typeChecker);
|
||||
log.debug('>>>> EXPORT: ' + exportDoc.name + ' (' + exportDoc.docType + ') from ' + moduleDoc.id);
|
||||
|
||||
exportDoc.members = [];
|
||||
exportDoc.statics = [];
|
||||
|
||||
// Generate docs for each of the export's members
|
||||
if (resolvedExport.flags & ts.SymbolFlags.HasMembers) {
|
||||
|
||||
for(var memberName in resolvedExport.members) {
|
||||
// FIXME(alexeagle): why do generic type params appear in members?
|
||||
if (memberName === 'T') {
|
||||
continue;
|
||||
}
|
||||
log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
|
||||
var memberSymbol = resolvedExport.members[memberName];
|
||||
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
|
||||
|
||||
// We special case the constructor and sort the other members alphabetically
|
||||
if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
|
||||
exportDoc.constructorDoc = memberDoc;
|
||||
docs.push(memberDoc);
|
||||
} else if (!hidePrivateMembers || memberSymbol.name.charAt(0) !== '_') {
|
||||
docs.push(memberDoc);
|
||||
exportDoc.members.push(memberDoc);
|
||||
} else if (memberSymbol.name === '__call' && memberSymbol.flags & ts.SymbolFlags.Signature) {
|
||||
docs.push(memberDoc);
|
||||
exportDoc.callMember = memberDoc;
|
||||
} else if (memberSymbol.name === '__new' && memberSymbol.flags & ts.SymbolFlags.Signature) {
|
||||
docs.push(memberDoc);
|
||||
exportDoc.newMember = memberDoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exportDoc.docType === 'enum') {
|
||||
for(var memberName in resolvedExport.exports) {
|
||||
log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
|
||||
var memberSymbol = resolvedExport.exports[memberName];
|
||||
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
|
||||
docs.push(memberDoc);
|
||||
exportDoc.members.push(memberDoc);
|
||||
}
|
||||
} else if (resolvedExport.flags & ts.SymbolFlags.HasExports) {
|
||||
for (var exported in resolvedExport.exports) {
|
||||
if (exported === 'prototype') continue;
|
||||
if (hidePrivateMembers && exported.charAt(0) === '_') continue;
|
||||
var memberSymbol = resolvedExport.exports[exported];
|
||||
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
|
||||
memberDoc.isStatic = true;
|
||||
docs.push(memberDoc);
|
||||
exportDoc.statics.push(memberDoc);
|
||||
}
|
||||
}
|
||||
|
||||
if (sortClassMembers) {
|
||||
exportDoc.members.sort(function(a, b) {
|
||||
if (a.name > b.name) return 1;
|
||||
if (a.name < b.name) return -1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Add this export doc to its module doc
|
||||
moduleDoc.exports.push(exportDoc);
|
||||
docs.push(exportDoc);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function createModuleDoc(moduleSymbol, basePath) {
|
||||
var id = moduleSymbol.name.replace(/^"|"$/g, '');
|
||||
var moduleDoc = {
|
||||
docType: 'module',
|
||||
id: id,
|
||||
aliases: [id],
|
||||
moduleTree: moduleSymbol,
|
||||
content: getContent(moduleSymbol),
|
||||
exports: [],
|
||||
fileInfo: getFileInfo(moduleSymbol, basePath),
|
||||
location: getLocation(moduleSymbol)
|
||||
};
|
||||
return moduleDoc;
|
||||
}
|
||||
|
||||
function createExportDoc(name, exportSymbol, moduleDoc, basePath, typeChecker) {
|
||||
var typeParamString = '';
|
||||
var heritageString = '';
|
||||
var typeDefinition = '';
|
||||
|
||||
exportSymbol.declarations.forEach(function(decl) {
|
||||
var sourceFile = ts.getSourceFileOfNode(decl);
|
||||
|
||||
if (decl.typeParameters) {
|
||||
typeParamString = '<' + getText(sourceFile, decl.typeParameters) + '>';
|
||||
}
|
||||
|
||||
if (decl.symbol.flags & ts.SymbolFlags.TypeAlias) {
|
||||
typeDefinition = getText(sourceFile, decl.type);
|
||||
}
|
||||
|
||||
if (decl.heritageClauses) {
|
||||
decl.heritageClauses.forEach(function(heritage) {
|
||||
|
||||
if (heritage.token == ts.SyntaxKind.ExtendsKeyword) {
|
||||
heritageString += " extends";
|
||||
heritage.types.forEach(function(typ, idx) {
|
||||
heritageString += (idx > 0 ? ',' : '') + typ.getFullText();
|
||||
});
|
||||
}
|
||||
|
||||
if (heritage.token == ts.SyntaxKind.ImplementsKeyword) {
|
||||
heritageString += " implements";
|
||||
heritage.types.forEach(function(typ, idx) {
|
||||
heritageString += (idx > 0 ? ', ' : '') + typ.getFullText();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//Make sure duplicate aliases aren't created, so "Ambiguous link" warnings are prevented
|
||||
var aliasNames = [name, moduleDoc.id + '/' + name];
|
||||
if (typeParamString) {
|
||||
aliasNames.push(name + typeParamString);
|
||||
aliasNames.push(moduleDoc.id + '/' + name + typeParamString);
|
||||
}
|
||||
|
||||
var exportDoc = {
|
||||
docType: getExportDocType(exportSymbol),
|
||||
name: name,
|
||||
id: moduleDoc.id + '/' + name,
|
||||
typeParams: typeParamString,
|
||||
heritage: heritageString,
|
||||
decorators: getDecorators(exportSymbol),
|
||||
aliases: aliasNames,
|
||||
moduleDoc: moduleDoc,
|
||||
content: getContent(exportSymbol),
|
||||
fileInfo: getFileInfo(exportSymbol, basePath),
|
||||
location: getLocation(exportSymbol)
|
||||
};
|
||||
|
||||
if (exportDoc.docType === 'var' || exportDoc.docType === 'const') {
|
||||
exportDoc.symbolTypeName = exportSymbol.valueDeclaration.type &&
|
||||
exportSymbol.valueDeclaration.type.typeName &&
|
||||
exportSymbol.valueDeclaration.type.typeName.text;
|
||||
}
|
||||
|
||||
if (exportDoc.docType === 'type-alias') {
|
||||
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
|
||||
}
|
||||
|
||||
if(exportSymbol.flags & ts.SymbolFlags.Function) {
|
||||
exportDoc.parameters = getParameters(typeChecker, exportSymbol);
|
||||
}
|
||||
if(exportSymbol.flags & ts.SymbolFlags.Value) {
|
||||
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
|
||||
}
|
||||
if (exportSymbol.flags & ts.SymbolFlags.TypeAlias) {
|
||||
exportDoc.typeDefinition = typeDefinition;
|
||||
}
|
||||
if (isAbstract(exportSymbol)) {
|
||||
exportDoc.abstract = true;
|
||||
}
|
||||
|
||||
// Compute the original module name from the relative file path
|
||||
exportDoc.originalModule = exportDoc.fileInfo.relativePath
|
||||
.replace(new RegExp('\.' + exportDoc.fileInfo.extension + '$'), '');
|
||||
|
||||
return exportDoc;
|
||||
}
|
||||
|
||||
function createMemberDoc(memberSymbol, classDoc, basePath, typeChecker) {
|
||||
var memberDoc = {
|
||||
docType: 'member',
|
||||
classDoc: classDoc,
|
||||
name: memberSymbol.name,
|
||||
decorators: getDecorators(memberSymbol),
|
||||
content: getContent(memberSymbol),
|
||||
fileInfo: getFileInfo(memberSymbol, basePath),
|
||||
location: getLocation(memberSymbol)
|
||||
};
|
||||
|
||||
memberDoc.typeParameters = getTypeParameters(typeChecker, memberSymbol);
|
||||
|
||||
if(memberSymbol.flags & (ts.SymbolFlags.Signature) ) {
|
||||
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
|
||||
memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
|
||||
switch(memberDoc.name) {
|
||||
case '__call':
|
||||
memberDoc.name = '';
|
||||
break;
|
||||
case '__new':
|
||||
memberDoc.name = 'new';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (memberSymbol.flags & ts.SymbolFlags.Method) {
|
||||
// NOTE: we use the property name `parameters` here so we don't conflict
|
||||
// with the `params` property that will be updated by dgeni reading the
|
||||
// `@param` tags from the docs
|
||||
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
|
||||
}
|
||||
|
||||
if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
|
||||
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
|
||||
memberDoc.name = 'constructor';
|
||||
}
|
||||
|
||||
if(memberSymbol.flags & ts.SymbolFlags.Value) {
|
||||
memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
|
||||
}
|
||||
|
||||
if(memberSymbol.flags & ts.SymbolFlags.Optional) {
|
||||
memberDoc.optional = true;
|
||||
}
|
||||
|
||||
return memberDoc;
|
||||
}
|
||||
|
||||
|
||||
function getDecorators(symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
|
||||
var decorators = declaration.decorators && declaration.decorators.map(function(decorator) {
|
||||
decorator = decorator.expression;
|
||||
return {
|
||||
name: decorator.expression ? decorator.expression.text : decorator.text,
|
||||
arguments: decorator.arguments && decorator.arguments.map(function(argument) {
|
||||
return getText(sourceFile, argument).trim();
|
||||
})
|
||||
};
|
||||
});
|
||||
return decorators;
|
||||
}
|
||||
|
||||
function getParameters(typeChecker, symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
if (!declaration.parameters) {
|
||||
var location = getLocation(symbol);
|
||||
throw new Error('missing declaration parameters for "' + symbol.name +
|
||||
'" in ' + sourceFile.fileName +
|
||||
' at line ' + location.start.line);
|
||||
}
|
||||
return declaration.parameters.map(function(parameter) {
|
||||
var paramText = '';
|
||||
if (parameter.dotDotDotToken) {
|
||||
paramText += '...';
|
||||
}
|
||||
paramText += getText(sourceFile, parameter.name);
|
||||
if (parameter.questionToken || parameter.initializer) {
|
||||
paramText += '?';
|
||||
}
|
||||
if (parameter.type) {
|
||||
paramText += ':' + getType(sourceFile, parameter.type);
|
||||
} else {
|
||||
paramText += ': any';
|
||||
if (parameter.dotDotDotToken) {
|
||||
paramText += '[]';
|
||||
}
|
||||
}
|
||||
return paramText.trim();
|
||||
});
|
||||
}
|
||||
|
||||
function getTypeParameters(typeChecker, symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
if (!declaration.typeParameters) return;
|
||||
var typeParams = declaration.typeParameters.map(function(type) {
|
||||
return getText(sourceFile, type).trim();
|
||||
});
|
||||
return typeParams;
|
||||
}
|
||||
|
||||
function getReturnType(typeChecker, symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
if (declaration.type) {
|
||||
return getType(sourceFile, declaration.type).trim();
|
||||
}
|
||||
}
|
||||
|
||||
function isAbstract(symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
return declaration.flags & ts.NodeFlags.Abstract;
|
||||
}
|
||||
|
||||
function expandSourceFiles(sourceFiles, basePath) {
|
||||
var filePaths = [];
|
||||
sourceFiles.forEach(function(sourcePattern) {
|
||||
filePaths = filePaths.concat(glob.sync(sourcePattern, { cwd: basePath }));
|
||||
});
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
|
||||
function getText(sourceFile, node) {
|
||||
return sourceFile.text.substring(node.pos, node.end);
|
||||
}
|
||||
|
||||
|
||||
// Strip any local renamed imports from the front of types
|
||||
function getType(sourceFile, type) {
|
||||
var text = getText(sourceFile, type);
|
||||
while (text.indexOf(".") >= 0) {
|
||||
// Keep namespaced symbols in RxNext
|
||||
if (text.match(/^\s*RxNext\./)) break;
|
||||
// handle the case List<thing.stuff> -> List<stuff>
|
||||
text = text.replace(/([^.<]*)\.([^>]*)/, "$2");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function getLocation(symbol) {
|
||||
var node = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(node);
|
||||
var location = {
|
||||
start: ts.getLineAndCharacterOfPosition(sourceFile, node.pos),
|
||||
end: ts.getLineAndCharacterOfPosition(sourceFile, node.end)
|
||||
};
|
||||
return location;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function convertToRegexCollection(items) {
|
||||
if (!items) return [];
|
||||
|
||||
// Must be an array
|
||||
if (!_.isArray(items)) {
|
||||
items = [items];
|
||||
}
|
||||
|
||||
// Convert string to exact matching regexes
|
||||
return items.map(function(item) {
|
||||
return _.isString(item) ? new RegExp('^' + item + '$') : item;
|
||||
});
|
||||
}
|
||||
|
||||
function anyMatches(regexes, item) {
|
||||
for(var i=0; i<regexes.length; ++i) {
|
||||
if ( item.match(regexes[i]) ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
127
docs/typescript-package/processors/readTypeScriptModules.spec.js
Normal file
127
docs/typescript-package/processors/readTypeScriptModules.spec.js
Normal file
@ -0,0 +1,127 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('readTypeScriptModules', function() {
|
||||
var dgeni, injector, processor;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
processor = injector.get('readTypeScriptModules');
|
||||
processor.basePath = path.resolve(__dirname, '../mocks/readTypeScriptModules');
|
||||
});
|
||||
|
||||
|
||||
describe('exportDocs', function() {
|
||||
it('should provide the original module if the export is re-exported', function() {
|
||||
processor.sourceFiles = [ 'publicModule.ts' ];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var exportedDoc = docs[1];
|
||||
expect(exportedDoc.originalModule).toEqual('privateModule');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ignoreExportsMatching', function() {
|
||||
it('should ignore exports that match items in the `ignoreExportsMatching` property', function() {
|
||||
processor.sourceFiles = [ 'ignoreExportsMatching.ts'];
|
||||
processor.ignoreExportsMatching = [/^_/];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
expect(moduleDoc.docType).toEqual('module');
|
||||
expect(moduleDoc.exports).toEqual([
|
||||
jasmine.objectContaining({ name: 'OKToExport' }),
|
||||
jasmine.objectContaining({ name: 'thisIsOK' })
|
||||
]);
|
||||
});
|
||||
|
||||
it('should only ignore `___esModule` exports by default', function() {
|
||||
processor.sourceFiles = [ 'ignoreExportsMatching.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
expect(moduleDoc.docType).toEqual('module');
|
||||
expect(getNames(moduleDoc.exports)).toEqual([
|
||||
'OKToExport',
|
||||
'_thisIsPrivate',
|
||||
'thisIsOK'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('interfaces', function() {
|
||||
|
||||
it('should mark optional properties', function() {
|
||||
processor.sourceFiles = [ 'interfaces.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
var exportedInterface = moduleDoc.exports[0];
|
||||
var member = exportedInterface.members[0];
|
||||
expect(member.name).toEqual('optionalProperty');
|
||||
expect(member.optional).toEqual(true);
|
||||
});
|
||||
|
||||
|
||||
it('should handle "call" type interfaces', function() {
|
||||
processor.sourceFiles = [ 'interfaces.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
var exportedInterface = moduleDoc.exports[0];
|
||||
|
||||
expect(exportedInterface.callMember).toBeDefined();
|
||||
expect(exportedInterface.callMember.parameters).toEqual(['param: T']);
|
||||
expect(exportedInterface.callMember.returnType).toEqual('U');
|
||||
expect(exportedInterface.callMember.typeParameters).toEqual(['T', 'U extends Findable<T>']);
|
||||
expect(exportedInterface.newMember).toBeDefined();
|
||||
expect(exportedInterface.newMember.parameters).toEqual(['param: number']);
|
||||
expect(exportedInterface.newMember.returnType).toEqual('MyInterface');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ordering of members', function() {
|
||||
it('should order class members in order of appearance (by default)', function() {
|
||||
processor.sourceFiles = ['orderingOfMembers.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
var classDoc = _.find(docs, { docType: 'class' });
|
||||
expect(classDoc.docType).toEqual('class');
|
||||
expect(getNames(classDoc.members)).toEqual([
|
||||
'firstItem',
|
||||
'otherMethod',
|
||||
'doStuff',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should not order class members if not sortClassMembers is false', function() {
|
||||
processor.sourceFiles = ['orderingOfMembers.ts'];
|
||||
processor.sortClassMembers = false;
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
var classDoc = _.find(docs, { docType: 'class' });
|
||||
expect(classDoc.docType).toEqual('class');
|
||||
expect(getNames(classDoc.members)).toEqual([
|
||||
'firstItem',
|
||||
'otherMethod',
|
||||
'doStuff'
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getNames(collection) {
|
||||
return collection.map(function(item) { return item.name; });
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function convertPrivateClassesToInterfaces() {
|
||||
return function(exportDocs, addInjectableReference) {
|
||||
_.forEach(exportDocs, function(exportDoc) {
|
||||
|
||||
// Search for classes with a constructor marked as `@internal`
|
||||
if (exportDoc.docType === 'class' && exportDoc.constructorDoc && exportDoc.constructorDoc.internal) {
|
||||
|
||||
// Convert this class to an interface with no constructor
|
||||
exportDoc.docType = 'interface';
|
||||
exportDoc.constructorDoc = null;
|
||||
|
||||
if (exportDoc.heritage) {
|
||||
// convert the heritage since interfaces use `extends` not `implements`
|
||||
exportDoc.heritage = exportDoc.heritage.replace('implements', 'extends');
|
||||
}
|
||||
|
||||
if (addInjectableReference) {
|
||||
// Add the `declare var SomeClass extends InjectableReference` construct
|
||||
exportDocs.push({
|
||||
docType: 'var',
|
||||
name: exportDoc.name,
|
||||
id: exportDoc.id,
|
||||
returnType: 'InjectableReference'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
@ -0,0 +1,76 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('readTypeScriptModules', function() {
|
||||
var dgeni, injector, convertPrivateClassesToInterfaces;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
convertPrivateClassesToInterfaces = injector.get('convertPrivateClassesToInterfaces');
|
||||
});
|
||||
|
||||
it('should convert @internal class docs to interface docs', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { internal: true }
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, false);
|
||||
expect(docs[0].docType).toEqual('interface');
|
||||
});
|
||||
|
||||
|
||||
it('should not touch non-internal class docs', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { }
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, false);
|
||||
expect(docs[0].docType).toEqual('class');
|
||||
});
|
||||
|
||||
|
||||
it('should convert the heritage since interfaces use `extends` not `implements`', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { internal: true },
|
||||
heritage: 'implements parentInterface'
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, false);
|
||||
expect(docs[0].heritage).toEqual('extends parentInterface');
|
||||
});
|
||||
|
||||
|
||||
it('should add new injectable reference types, if specified, to the passed in collection', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { internal: true },
|
||||
heritage: 'implements parentInterface'
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, true);
|
||||
expect(docs[1]).toEqual({
|
||||
docType : 'var',
|
||||
name : 'privateClass',
|
||||
id : 'privateClass',
|
||||
returnType : 'InjectableReference'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,75 @@
|
||||
var ts = require('typescript');
|
||||
var fs = require('fs');
|
||||
var path = require('canonical-path');
|
||||
|
||||
// We need to provide our own version of CompilerHost because we want to set the
|
||||
// base directory and specify what extensions to consider when trying to load a source
|
||||
// file
|
||||
module.exports = function createCompilerHost(log) {
|
||||
|
||||
return function createCompilerHost(options, baseDir, extensions) {
|
||||
|
||||
return {
|
||||
getSourceFile: function(fileName, languageVersion, onError) {
|
||||
var text, resolvedPath, resolvedPathWithExt;
|
||||
|
||||
// Strip off the extension and resolve relative to the baseDir
|
||||
baseFilePath = fileName.replace(/\.[^.]+$/, '');
|
||||
resolvedPath = path.resolve(baseDir, baseFilePath);
|
||||
|
||||
// Iterate through each possible extension and return the first source file that is actually found
|
||||
for(var i=0; i<extensions.length; i++) {
|
||||
|
||||
// Try reading the content from files using each of the given extensions
|
||||
try {
|
||||
resolvedPathWithExt = resolvedPath + extensions[i];
|
||||
log.silly('getSourceFile:', resolvedPathWithExt);
|
||||
text = fs.readFileSync(resolvedPathWithExt, { encoding: options.charset });
|
||||
log.debug('found source file:', fileName, resolvedPathWithExt);
|
||||
return ts.createSourceFile(baseFilePath + extensions[i], text, languageVersion);
|
||||
}
|
||||
catch(e) {
|
||||
// Try again if the file simply did not exist, otherwise report the error as a warning
|
||||
if(e.code !== 'ENOENT') {
|
||||
if (onError) onError(e.message);
|
||||
log.warn('Error reading ' + resolvedPathWithExt + ' : ' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getDefaultLibFileName: function(options) {
|
||||
return path.resolve(path.dirname(ts.sys.getExecutingFilePath()), ts.getDefaultLibFileName(options));
|
||||
},
|
||||
writeFile: function(fileName, data, writeByteOrderMark, onError) {
|
||||
// no-op
|
||||
},
|
||||
getCurrentDirectory: function() {
|
||||
return baseDir;
|
||||
},
|
||||
useCaseSensitiveFileNames: function() {
|
||||
return ts.sys.useCaseSensitiveFileNames;
|
||||
},
|
||||
getCanonicalFileName: function(fileName) {
|
||||
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
|
||||
// otherwise use toLowerCase as a canonical form.
|
||||
return ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
},
|
||||
getNewLine: function() {
|
||||
return ts.sys.newLine;
|
||||
},
|
||||
fileExists: function(fileName) {
|
||||
var resolvedPath = path.resolve(baseDir, fileName);
|
||||
try {
|
||||
fs.statSync(resolvedPath);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
readFile: function(fileName) {
|
||||
var resolvedPath = path.resolve(baseDir, fileName);
|
||||
return fs.readFileSync(resolvedPath, { encoding: options.charset });
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,80 @@
|
||||
var mockPackage = require('../../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
var ts = require('typescript');
|
||||
|
||||
describe('createCompilerHost', function() {
|
||||
var dgeni, injector, options, host, baseDir, extensions;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
var createCompilerHost = injector.get('createCompilerHost');
|
||||
|
||||
options = { charset: 'utf8' };
|
||||
baseDir = path.resolve(__dirname, '../../mocks/tsParser');
|
||||
extensions = ['.ts', '.js'];
|
||||
|
||||
host = createCompilerHost(options, baseDir, extensions);
|
||||
});
|
||||
|
||||
describe('getSourceFile', function() {
|
||||
it('should return a SourceFile object for a given path, with fileName relative to baseDir', function() {
|
||||
var sourceFile = host.getSourceFile('testSrc.ts');
|
||||
expect(sourceFile.fileName).toEqual('testSrc.ts');
|
||||
expect(sourceFile.pos).toEqual(0);
|
||||
expect(sourceFile.text).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
it('should try each of the configured extensions and update the filename to the correct extension', function() {
|
||||
var sourceFile = host.getSourceFile('testSrc.js');
|
||||
expect(sourceFile.fileName).toEqual('testSrc.ts');
|
||||
|
||||
sourceFile = host.getSourceFile('../mockPackage.ts');
|
||||
expect(sourceFile.fileName).toEqual('../mockPackage.js');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getDefaultLibFileName', function() {
|
||||
it('should return a path to the default library', function() {
|
||||
expect(host.getDefaultLibFileName(options)).toContain('typescript/lib/lib.d.ts');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('writeFile', function() {
|
||||
it('should do nothing', function() {
|
||||
host.writeFile();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getCurrentDirectory', function() {
|
||||
it('should return the baseDir', function() {
|
||||
expect(host.getCurrentDirectory()).toEqual(baseDir);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('useCaseSensitiveFileNames', function() {
|
||||
it('should return true if the OS is case sensitive', function() {
|
||||
expect(host.useCaseSensitiveFileNames()).toBe(ts.sys.useCaseSensitiveFileNames);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getCanonicalFileName', function() {
|
||||
it('should lower case the filename', function() {
|
||||
var expectedFilePath = host.useCaseSensitiveFileNames() ? 'SomeFile.ts' : 'somefile.ts';
|
||||
expect(host.getCanonicalFileName('SomeFile.ts')).toEqual(expectedFilePath);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getNewLine', function() {
|
||||
it('should return the newline character for the OS', function() {
|
||||
expect(host.getNewLine()).toEqual(require('os').EOL);
|
||||
});
|
||||
});
|
||||
});
|
49
docs/typescript-package/services/tsParser/getContent.js
Normal file
49
docs/typescript-package/services/tsParser/getContent.js
Normal file
@ -0,0 +1,49 @@
|
||||
var ts = require('typescript');
|
||||
var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
|
||||
|
||||
module.exports = function getContent() {
|
||||
return function(symbol) {
|
||||
|
||||
var content = "";
|
||||
|
||||
if (!symbol.declarations) return content;
|
||||
|
||||
symbol.declarations.forEach(function(declaration) {
|
||||
|
||||
// If this is left side of dotted module declaration, there is no doc comment associated with this declaration
|
||||
if (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.body.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// If this is dotted module name, get the doc comments from the parent
|
||||
while (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.parent.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
declaration = declaration.parent;
|
||||
}
|
||||
|
||||
// If this is a variable declaration then we get the doc comments from the grand parent
|
||||
if (declaration.kind === ts.SyntaxKind.VariableDeclaration) {
|
||||
declaration = declaration.parent.parent;
|
||||
}
|
||||
|
||||
// Get the source file of this declaration
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
var commentRanges = ts.getJsDocComments(declaration, sourceFile);
|
||||
|
||||
if (commentRanges) {
|
||||
commentRanges.forEach(function(commentRange) {
|
||||
content += sourceFile.text
|
||||
.substring(commentRange.pos+ '/**'.length, commentRange.end - '*/'.length)
|
||||
.replace(LEADING_STAR, '')
|
||||
.trim();
|
||||
if (commentRange.hasTrailingNewLine) {
|
||||
content += '\n';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
content += '\n';
|
||||
});
|
||||
|
||||
return content;
|
||||
};
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
var ts = require('typescript');
|
||||
|
||||
module.exports = function getExportDocType(log) {
|
||||
|
||||
return function(symbol) {
|
||||
if(symbol.flags & ts.SymbolFlags.Function) {
|
||||
return 'function';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.Class) {
|
||||
return 'class';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.Interface) {
|
||||
return 'interface';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.ConstEnum) {
|
||||
return 'enum';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.RegularEnum) {
|
||||
return 'enum';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.Property) {
|
||||
return 'module-property';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.TypeAlias) {
|
||||
return 'type-alias';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.FunctionScopedVariable) {
|
||||
return 'var';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.BlockScopedVariable) {
|
||||
return getBlockScopedVariableDocType(symbol);
|
||||
}
|
||||
|
||||
log.warn('getExportDocType(): Unknown symbol type', {
|
||||
symbolName: symbol.name,
|
||||
symbolType: symbol.flags,
|
||||
symbolTarget: symbol.target,
|
||||
file: ts.getSourceFileOfNode(symbol.declarations[0]).fileName
|
||||
});
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
function getBlockScopedVariableDocType(symbol) {
|
||||
|
||||
var node = symbol.valueDeclaration;
|
||||
while(node) {
|
||||
if ( node.flags & 0x2000 /* const */) {
|
||||
return 'const';
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return 'let';
|
||||
}
|
||||
};
|
20
docs/typescript-package/services/tsParser/getFileInfo.js
Normal file
20
docs/typescript-package/services/tsParser/getFileInfo.js
Normal file
@ -0,0 +1,20 @@
|
||||
var path = require('canonical-path');
|
||||
var ts = require('typescript');
|
||||
|
||||
module.exports = function getFileInfo(log) {
|
||||
|
||||
return function (symbol, basePath) {
|
||||
var fileName = ts.getSourceFileOfNode(symbol.declarations[0]).fileName;
|
||||
|
||||
var file = path.resolve(basePath, fileName);
|
||||
var fileInfo = {
|
||||
filePath: file,
|
||||
baseName: path.basename(file, path.extname(file)),
|
||||
extension: path.extname(file).replace(/^\./, ''),
|
||||
basePath: basePath,
|
||||
relativePath: fileName,
|
||||
projectRelativePath: fileName
|
||||
};
|
||||
return fileInfo;
|
||||
};
|
||||
};
|
74
docs/typescript-package/services/tsParser/index.js
Normal file
74
docs/typescript-package/services/tsParser/index.js
Normal file
@ -0,0 +1,74 @@
|
||||
var ts = require('typescript');
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = function tsParser(createCompilerHost, log) {
|
||||
|
||||
return {
|
||||
|
||||
// These are the extension that we should consider when trying to load a module
|
||||
// During migration from Traceur, there is a mix of `.ts`, `.es6` and `.js` (atScript)
|
||||
// files in the project and the TypeScript compiler only looks for `.ts` files when trying
|
||||
// to load imports.
|
||||
extensions: ['.ts', '.js'],
|
||||
|
||||
// The options for the TS compiler
|
||||
options: {
|
||||
allowNonTsExtensions: true,
|
||||
charset: 'utf8'
|
||||
},
|
||||
|
||||
parse: function(fileNames, baseDir) {
|
||||
|
||||
// "Compile" a program from the given module filenames, to get hold of a
|
||||
// typeChecker that can be used to interrogate the modules, exports and so on.
|
||||
var host = createCompilerHost(this.options, baseDir, this.extensions);
|
||||
var program = ts.createProgram(fileNames, this.options, host);
|
||||
var typeChecker = program.getTypeChecker();
|
||||
|
||||
// Create an array of module symbols for each file we were given
|
||||
var moduleSymbols = [];
|
||||
fileNames.forEach(function(fileName) {
|
||||
var sourceFile = program.getSourceFile(fileName);
|
||||
|
||||
if (!sourceFile) {
|
||||
throw new Error('Invalid source file: ' + fileName);
|
||||
} else if (!sourceFile.symbol) {
|
||||
// Some files contain only a comment and no actual module code
|
||||
log.warn('No module code found in ' + fileName);
|
||||
} else {
|
||||
moduleSymbols.push(sourceFile.symbol);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
moduleSymbols.forEach(function(tsModule) {
|
||||
|
||||
// The type checker has a nice helper function that returns an array of Symbols
|
||||
// representing the exports for a given module
|
||||
tsModule.exportArray = typeChecker.getExportsOfModule(tsModule);
|
||||
|
||||
// Although 'star' imports (e.g. `export * from 'some/module';) get resolved automatically
|
||||
// by the compiler/binder, it seems that explicit imports (e.g. `export {SomeClass} from 'some/module'`)
|
||||
// do not so we have to do a little work.
|
||||
tsModule.exportArray.forEach(function(moduleExport) {
|
||||
if (moduleExport.flags & ts.SymbolFlags.Alias) {
|
||||
// To maintain the alias information (particularly the alias name)
|
||||
// we just attach the original "resolved" symbol to the alias symbol
|
||||
moduleExport.resolvedSymbol = typeChecker.getAliasedSymbol(moduleExport);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
moduleSymbols.typeChecker = typeChecker;
|
||||
|
||||
return {
|
||||
moduleSymbols: moduleSymbols,
|
||||
typeChecker: typeChecker,
|
||||
program: program,
|
||||
host: host
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
21
docs/typescript-package/services/tsParser/index.spec.js
Normal file
21
docs/typescript-package/services/tsParser/index.spec.js
Normal file
@ -0,0 +1,21 @@
|
||||
var mockPackage = require('../../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
|
||||
describe('tsParser', function() {
|
||||
var dgeni, injector, parser;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
parser = injector.get('tsParser');
|
||||
});
|
||||
|
||||
it("should parse a TS file", function() {
|
||||
var parseInfo = parser.parse(['testSrc.ts'], path.resolve(__dirname, '../../mocks/tsParser'));
|
||||
var tsModules = parseInfo.moduleSymbols;
|
||||
expect(tsModules.length).toEqual(1);
|
||||
expect(tsModules[0].exportArray.length).toEqual(3);
|
||||
expect(tsModules[0].exportArray.map(function(i) { return i.name; })).toEqual(['MyClass', 'myFn', 'x']);
|
||||
});
|
||||
});
|
1670
gulpfile.js
1670
gulpfile.js
File diff suppressed because it is too large
Load Diff
86
karma-dart-evalcache.js
Normal file
86
karma-dart-evalcache.js
Normal file
@ -0,0 +1,86 @@
|
||||
// This module provides a customFileHandler for karma
|
||||
// that serves files with urls like /packages_<timestamp>/...
|
||||
// with maximum cache.
|
||||
// We are using these urls when we spawn isolates
|
||||
// so that the isolates don't reload files every time.
|
||||
|
||||
var common = require('karma/lib/middleware/common');
|
||||
var fs = require('fs');
|
||||
|
||||
var DART_EVAL_PATH_RE = /.*\/packages_\d+\/(.*)$/;
|
||||
|
||||
module.exports = createFactory;
|
||||
|
||||
function createFactory(proxyPaths) {
|
||||
return {
|
||||
'framework:dart-evalcache': ['factory', dartEvalCacheFactory]
|
||||
};
|
||||
|
||||
function dartEvalCacheFactory(emitter, logger, customFileHandlers) {
|
||||
var filesPromise = new common.PromiseContainer();
|
||||
emitter.on('file_list_modified', function(files) {
|
||||
filesPromise.set(Promise.resolve(files));
|
||||
});
|
||||
|
||||
var serveFile = common.createServeFile(fs);
|
||||
var log = logger.create('dart-evalcache');
|
||||
|
||||
customFileHandlers.push({
|
||||
urlRegex: DART_EVAL_PATH_RE,
|
||||
handler: handler
|
||||
});
|
||||
|
||||
// See source_files handler
|
||||
function handler(request, response, fa, fb, basePath) {
|
||||
return filesPromise.then(function(files) {
|
||||
try {
|
||||
var requestedFilePath = mapUrlToFile(request.url, proxyPaths, basePath, log);
|
||||
// TODO(vojta): change served to be a map rather then an array
|
||||
var file = findByPath(files.served, requestedFilePath);
|
||||
if (file) {
|
||||
serveFile(file.contentPath || file.path, response, function() {
|
||||
common.setHeavyCacheHeaders(response);
|
||||
}, file.content);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end('Not found');
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e.stack);
|
||||
response.writeHead(500);
|
||||
response.end('Error', e.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mapUrlToFile(url, proxyPaths, basePath, log) {
|
||||
var originalUrl = url;
|
||||
url = url.indexOf('?') > -1 ? url.substring(0, url.indexOf('?')) : url;
|
||||
var match = DART_EVAL_PATH_RE.exec(url);
|
||||
var packagePath = match[1];
|
||||
var result = null;
|
||||
var lastProxyFromLength = 0;
|
||||
Object.keys(proxyPaths).forEach(function(proxyFrom) {
|
||||
if (startsWith(packagePath, proxyFrom) && proxyFrom.length > lastProxyFromLength) {
|
||||
lastProxyFromLength = proxyFrom.length;
|
||||
result = proxyPaths[proxyFrom] + packagePath.substring(proxyFrom.length);
|
||||
}
|
||||
});
|
||||
return basePath + '/' + result;
|
||||
}
|
||||
|
||||
function startsWith(string, subString) {
|
||||
return string.length >= subString.length && string.slice(0, subString.length) === subString;
|
||||
}
|
||||
|
||||
function findByPath(files, path) {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (files[i].path === path) {
|
||||
return files[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -1,93 +1,82 @@
|
||||
var sauceConf = require('./sauce.conf');
|
||||
|
||||
var packageSources = {
|
||||
// Dependencies installed with `pub install`.
|
||||
'unittest': 'packages/unittest',
|
||||
'guinness': 'packages/guinness',
|
||||
'matcher': 'packages/matcher',
|
||||
'stack_trace': 'packages/stack_trace',
|
||||
'collection': 'packages/collection',
|
||||
'path': 'packages/path',
|
||||
'observe': 'packages/observe',
|
||||
'quiver': 'packages/quiver',
|
||||
'intl': 'packages/intl',
|
||||
'smoke': 'packages/smoke',
|
||||
'logging': 'packages/logging',
|
||||
'utf': 'packages/utf',
|
||||
|
||||
// Local dependencies, transpiled from the source.
|
||||
'angular2': 'dist/dart/angular2/lib',
|
||||
'angular2/test/': 'dist/dart/angular2/test/',
|
||||
'http': 'dist/dart/http/lib',
|
||||
'angular2_material': 'dist/dart/angular2_material/lib',
|
||||
'benchpress': 'dist/dart/benchpress/lib',
|
||||
'examples': 'dist/dart/examples/lib'
|
||||
};
|
||||
|
||||
var proxyPaths = {};
|
||||
Object.keys(packageSources).map(function(packageName) {
|
||||
var filePath = packageSources[packageName];
|
||||
proxyPaths['/packages/'+packageName] = '/base/'+filePath;
|
||||
});
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
var file2moduleName = require('./tools/build/file2modulename');
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
frameworks: ['dart-unittest'],
|
||||
frameworks: ['dart-unittest', 'dart-evalcache'],
|
||||
|
||||
files: [
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-init.dart', included: true},
|
||||
// Unit test files needs to be included.
|
||||
// Karma-dart generates `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'modules/*/test/**/*_spec.js', included: true},
|
||||
{pattern: 'tools/transpiler/spec/**/*_spec.js', included: true},
|
||||
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: false},
|
||||
|
||||
// These files are not included, they are imported by the unit tests above.
|
||||
{pattern: 'modules/**', included: false},
|
||||
{pattern: 'tools/transpiler/spec/**/*', included: false},
|
||||
// Karma-dart via the dart-unittest framework generates
|
||||
// `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'dist/dart/**', included: false, watched: false},
|
||||
|
||||
// Dependencies, installed with `pub install`.
|
||||
{pattern: 'packages/**/*.dart', included: false, watched: false},
|
||||
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-main.dart', included: true}
|
||||
{pattern: 'test-main.dart', included: true},
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false},
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/dart/**/packages/**',
|
||||
'modules/angular1_router/**'
|
||||
],
|
||||
|
||||
karmaDartImports: {
|
||||
guinness: 'package:guinness/guinness_html.dart'
|
||||
},
|
||||
|
||||
// TODO(vojta): Remove the localhost:9877 from urls, once the proxy fix is merged:
|
||||
// https://github.com/karma-runner/karma/pull/1207
|
||||
//
|
||||
// Map packages to the correct urls where Karma serves them.
|
||||
proxies: {
|
||||
// Dependencies installed with `pub install`.
|
||||
'/packages/unittest': 'http://localhost:9877/base/packages/unittest',
|
||||
'/packages/guinness': 'http://localhost:9877/base/packages/guinness',
|
||||
'/packages/matcher': 'http://localhost:9877/base/packages/matcher',
|
||||
'/packages/stack_trace': 'http://localhost:9877/base/packages/stack_trace',
|
||||
'/packages/collection': 'http://localhost:9877/base/packages/collection',
|
||||
'/packages/path': 'http://localhost:9877/base/packages/path',
|
||||
proxies: proxyPaths,
|
||||
|
||||
// Local dependencies, transpiled from the source.
|
||||
'/packages/angular': 'http://localhost:9877/base/modules/angular',
|
||||
'/packages/benchpress': 'http://localhost:9877/base/modules/benchpress',
|
||||
'/packages/core': 'http://localhost:9877/base/modules/core',
|
||||
'/packages/change_detection': 'http://localhost:9877/base/modules/change_detection',
|
||||
'/packages/reflection': 'http://localhost:9877/base/modules/reflection',
|
||||
'/packages/di': 'http://localhost:9877/base/modules/di',
|
||||
'/packages/directives': 'http://localhost:9877/base/modules/directives',
|
||||
'/packages/facade': 'http://localhost:9877/base/modules/facade',
|
||||
'/packages/forms': 'http://localhost:9877/base/modules/forms',
|
||||
'/packages/test_lib': 'http://localhost:9877/base/modules/test_lib',
|
||||
'/packages/mock': 'http://localhost:9877/base/modules/mock',
|
||||
},
|
||||
|
||||
preprocessors: {
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'tools/**/*.js': ['traceur']
|
||||
},
|
||||
|
||||
traceurPreprocessor: {
|
||||
options: {
|
||||
outputLanguage: 'dart',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
modules: 'register',
|
||||
memberVariables: true,
|
||||
types: true,
|
||||
// typeAssertions: true,
|
||||
// typeAssertionModule: 'assert',
|
||||
annotations: true
|
||||
},
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace('.js', '.dart');
|
||||
}
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
DartiumWithWebPlatform: {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] }
|
||||
},
|
||||
customLaunchers: sauceConf.customLaunchers,
|
||||
browsers: ['DartiumWithWebPlatform'],
|
||||
|
||||
port: 9877
|
||||
port: 9877,
|
||||
|
||||
plugins: [
|
||||
require('karma-dart'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-sauce-launcher'),
|
||||
require('./karma-dart-evalcache')(packageSources)
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
var sauceConf = require('./sauce.conf');
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
var file2moduleName = require('./tools/build/file2modulename');
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
@ -9,60 +9,57 @@ module.exports = function(config) {
|
||||
|
||||
files: [
|
||||
// Sources and specs.
|
||||
// Loaded through the es6-module-loader, in `test-main.js`.
|
||||
{pattern: 'modules/**', included: false},
|
||||
{pattern: 'tools/transpiler/spec/**', included: false},
|
||||
// Loaded through the System loader, in `test-main.js`.
|
||||
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
|
||||
|
||||
'node_modules/es6-shim/es6-shim.js',
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
|
||||
// zone-microtask must be included first as it contains a Promise monkey patch
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
|
||||
'node_modules/traceur/bin/traceur-runtime.js',
|
||||
'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'modules/angular2/src/testing/shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
'node_modules/systemjs/lib/extension-register.js',
|
||||
'node_modules/zone.js/zone.js',
|
||||
'node_modules/zone.js/long-stack-trace-zone.js',
|
||||
|
||||
{pattern: 'node_modules/@reactivex/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js'
|
||||
'test-main.js',
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'modules/**/e2e_test/**'
|
||||
],
|
||||
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/angular1_router.js'],
|
||||
|
||||
preprocessors: {
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'modules/**/*.es6': ['traceur'],
|
||||
'tools/transpiler/spec/**/*.js': ['traceur'],
|
||||
'tools/transpiler/spec/**/*.es6': ['traceur'],
|
||||
},
|
||||
customLaunchers: sauceConf.customLaunchers,
|
||||
|
||||
traceurPreprocessor: {
|
||||
sauceLabs: {
|
||||
testName: 'Angular2',
|
||||
startConnect: false,
|
||||
recordVideo: false,
|
||||
recordScreenshots: false,
|
||||
options: {
|
||||
outputLanguage: 'es5',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
memberVariables: true,
|
||||
modules: 'instantiate',
|
||||
types: true,
|
||||
typeAssertions: true,
|
||||
typeAssertionModule: 'rtts_assert/rtts_assert',
|
||||
annotations: true
|
||||
},
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace(/\.es6$/, '.js');
|
||||
'selenium-version': '2.47.1',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
}
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
DartiumWithWebPlatform: {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] }
|
||||
},
|
||||
browsers: ['ChromeCanary'],
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876
|
||||
});
|
||||
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
if (process.env.TRAVIS && process.env.MODE === 'saucelabs') {
|
||||
config.sauceLabs.build = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
// TODO(mlaval): remove once SauceLabs supports websockets.
|
||||
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||
console.log('>>>> setting socket.io transport to polling <<<<');
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
};
|
||||
|
@ -1 +0,0 @@
|
||||
karma-js.conf.js
|
67
modules/angular1_router/build.js
vendored
Normal file
67
modules/angular1_router/build.js
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var ts = require('typescript');
|
||||
|
||||
var files = [
|
||||
'lifecycle_annotations_impl.ts',
|
||||
'url_parser.ts',
|
||||
'path_recognizer.ts',
|
||||
'route_config_impl.ts',
|
||||
'async_route_handler.ts',
|
||||
'sync_route_handler.ts',
|
||||
'route_recognizer.ts',
|
||||
'instruction.ts',
|
||||
'route_config_nomalizer.ts',
|
||||
'route_lifecycle_reflector.ts',
|
||||
'route_registry.ts',
|
||||
'router.ts'
|
||||
];
|
||||
|
||||
var PRELUDE = '(function(){\n';
|
||||
var POSTLUDE = '\n}());\n';
|
||||
var FACADES = fs.readFileSync(__dirname + '/lib/facades.es5', 'utf8');
|
||||
var DIRECTIVES = fs.readFileSync(__dirname + '/src/ng_outlet.ts', 'utf8');
|
||||
var moduleTemplate = fs.readFileSync(__dirname + '/src/module_template.js', 'utf8');
|
||||
|
||||
function main() {
|
||||
var dir = __dirname + '/../angular2/src/router/';
|
||||
var sharedCode = files.reduce(function (prev, file) {
|
||||
return prev + transform(fs.readFileSync(dir + file, 'utf8'));
|
||||
}, '');
|
||||
|
||||
var out = moduleTemplate.replace('//{{FACADES}}', FACADES).replace('//{{SHARED_CODE}}', sharedCode);
|
||||
return PRELUDE + transform(DIRECTIVES) + out + POSTLUDE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a directory name and a file's TypeScript content, return an object with the ES5 code,
|
||||
* sourcemap, and exported variable identifier name for the content.
|
||||
*/
|
||||
var IMPORT_RE = new RegExp("import \\{?([\\w\\n_, ]+)\\}? from '(.+)';?", 'g');
|
||||
function transform(contents) {
|
||||
contents = contents.replace(IMPORT_RE, function (match, imports, includePath) {
|
||||
//TODO: remove special-case
|
||||
if (isFacadeModule(includePath) || includePath === './router_outlet') {
|
||||
return '';
|
||||
}
|
||||
return match;
|
||||
});
|
||||
return ts.transpile(contents, {
|
||||
target: ts.ScriptTarget.ES5,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
}
|
||||
|
||||
function isFacadeModule(modulePath) {
|
||||
return modulePath.indexOf('facade') > -1 ||
|
||||
modulePath === 'angular2/src/core/reflection/reflection';
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
var dist = __dirname + '/../../dist';
|
||||
if (!fs.existsSync(dist)) {
|
||||
fs.mkdirSync(dist);
|
||||
}
|
||||
fs.writeFileSync(dist + '/angular_1_router.js', main());
|
||||
};
|
22
modules/angular1_router/index.html
Normal file
22
modules/angular1_router/index.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div ng-app="myApp" ng-controller="MyCtrl">
|
||||
|
||||
</div>
|
||||
<script src="../../node_modules/angular/angular.js"></script>
|
||||
<script src="../../dist/angular_1_router.js"></script>
|
||||
<script>
|
||||
angular.module('myApp', ['ngComponentRouter'])
|
||||
.controller('MyCtrl', ['$router', function ($router) {
|
||||
console.log($router);
|
||||
$router.navigateByUrl('/')
|
||||
.then(console.log.bind(console, 'resolve'), console.log.bind(console, 'reject'));
|
||||
}]);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
29
modules/angular1_router/karma-router.conf.js
Normal file
29
modules/angular1_router/karma-router.conf.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var sauceConf = require('../../sauce.conf');
|
||||
|
||||
// This runs the tests for the router in Angular 1.x
|
||||
|
||||
module.exports = function (config) {
|
||||
var options = {
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
files: [
|
||||
'../../node_modules/angular/angular.js',
|
||||
'../../node_modules/angular-animate/angular-animate.js',
|
||||
'../../node_modules/angular-mocks/angular-mocks.js',
|
||||
|
||||
'../../dist/angular_1_router.js',
|
||||
'src/ng_route_shim.js',
|
||||
|
||||
'test/*.es5.js',
|
||||
'test/**/*_spec.js'
|
||||
],
|
||||
|
||||
customLaunchers: sauceConf.customLaunchers,
|
||||
|
||||
browsers: ['ChromeCanary']
|
||||
};
|
||||
|
||||
config.set(options);
|
||||
};
|
307
modules/angular1_router/lib/facades.es5
Normal file
307
modules/angular1_router/lib/facades.es5
Normal file
@ -0,0 +1,307 @@
|
||||
function CONST() {
|
||||
return (function(target) {
|
||||
return target;
|
||||
});
|
||||
}
|
||||
|
||||
function CONST_EXPR(expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
function isPresent (x) {
|
||||
return !!x;
|
||||
}
|
||||
|
||||
function isBlank (x) {
|
||||
return !x;
|
||||
}
|
||||
|
||||
function isString(obj) {
|
||||
return typeof obj === 'string';
|
||||
}
|
||||
|
||||
function isType (x) {
|
||||
return typeof x === 'function';
|
||||
}
|
||||
|
||||
function isStringMap(obj) {
|
||||
return typeof obj === 'object' && obj !== null;
|
||||
}
|
||||
|
||||
function isArray(obj) {
|
||||
return Array.isArray(obj);
|
||||
}
|
||||
|
||||
function getTypeNameForDebugging (fn) {
|
||||
return fn.name || 'Root';
|
||||
}
|
||||
|
||||
var PromiseWrapper = {
|
||||
resolve: function (reason) {
|
||||
return $q.when(reason);
|
||||
},
|
||||
|
||||
reject: function (reason) {
|
||||
return $q.reject(reason);
|
||||
},
|
||||
|
||||
catchError: function (promise, fn) {
|
||||
return promise.then(null, fn);
|
||||
},
|
||||
all: function (promises) {
|
||||
return $q.all(promises);
|
||||
}
|
||||
};
|
||||
|
||||
var RegExpWrapper = {
|
||||
create: function(regExpStr, flags) {
|
||||
flags = flags ? flags.replace(/g/g, '') : '';
|
||||
return new RegExp(regExpStr, flags + 'g');
|
||||
},
|
||||
firstMatch: function(regExp, input) {
|
||||
regExp.lastIndex = 0;
|
||||
return regExp.exec(input);
|
||||
},
|
||||
matcher: function (regExp, input) {
|
||||
regExp.lastIndex = 0;
|
||||
return { re: regExp, input: input };
|
||||
}
|
||||
};
|
||||
|
||||
var reflector = {
|
||||
annotations: function (fn) {
|
||||
//TODO: implement me
|
||||
return fn.annotations || [];
|
||||
}
|
||||
};
|
||||
|
||||
var MapWrapper = {
|
||||
create: function() {
|
||||
return new Map();
|
||||
},
|
||||
|
||||
get: function(m, k) {
|
||||
return m.get(k);
|
||||
},
|
||||
|
||||
set: function(m, k, v) {
|
||||
return m.set(k, v);
|
||||
},
|
||||
|
||||
contains: function (m, k) {
|
||||
return m.has(k);
|
||||
},
|
||||
|
||||
forEach: function (m, fn) {
|
||||
return m.forEach(fn);
|
||||
}
|
||||
};
|
||||
|
||||
var StringMapWrapper = {
|
||||
create: function () {
|
||||
return {};
|
||||
},
|
||||
|
||||
set: function (m, k, v) {
|
||||
return m[k] = v;
|
||||
},
|
||||
|
||||
get: function (m, k) {
|
||||
return m.hasOwnProperty(k) ? m[k] : undefined;
|
||||
},
|
||||
|
||||
contains: function (m, k) {
|
||||
return m.hasOwnProperty(k);
|
||||
},
|
||||
|
||||
keys: function(map) {
|
||||
return Object.keys(map);
|
||||
},
|
||||
|
||||
isEmpty: function(map) {
|
||||
for (var prop in map) {
|
||||
if (map.hasOwnProperty(prop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
delete: function(map, key) {
|
||||
delete map[key];
|
||||
},
|
||||
|
||||
forEach: function (m, fn) {
|
||||
for (var prop in m) {
|
||||
if (m.hasOwnProperty(prop)) {
|
||||
fn(m[prop], prop);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
equals: function (m1, m2) {
|
||||
var k1 = Object.keys(m1);
|
||||
var k2 = Object.keys(m2);
|
||||
if (k1.length != k2.length) {
|
||||
return false;
|
||||
}
|
||||
var key;
|
||||
for (var i = 0; i < k1.length; i++) {
|
||||
key = k1[i];
|
||||
if (m1[key] !== m2[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
merge: function(m1, m2) {
|
||||
var m = {};
|
||||
for (var attr in m1) {
|
||||
if (m1.hasOwnProperty(attr)) {
|
||||
m[attr] = m1[attr];
|
||||
}
|
||||
}
|
||||
for (var attr in m2) {
|
||||
if (m2.hasOwnProperty(attr)) {
|
||||
m[attr] = m2[attr];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
};
|
||||
|
||||
var List = Array;
|
||||
var ListWrapper = {
|
||||
create: function () {
|
||||
return [];
|
||||
},
|
||||
|
||||
push: function (l, v) {
|
||||
return l.push(v);
|
||||
},
|
||||
|
||||
forEach: function (l, fn) {
|
||||
return l.forEach(fn);
|
||||
},
|
||||
|
||||
first: function(array) {
|
||||
if (!array)
|
||||
return null;
|
||||
return array[0];
|
||||
},
|
||||
|
||||
map: function (l, fn) {
|
||||
return l.map(fn);
|
||||
},
|
||||
|
||||
join: function (l, str) {
|
||||
return l.join(str);
|
||||
},
|
||||
|
||||
reduce: function(list, fn, init) {
|
||||
return list.reduce(fn, init);
|
||||
},
|
||||
|
||||
filter: function(array, pred) {
|
||||
return array.filter(pred);
|
||||
},
|
||||
|
||||
concat: function(a, b) {
|
||||
return a.concat(b);
|
||||
},
|
||||
|
||||
slice: function(l) {
|
||||
var from = arguments[1] !== (void 0) ? arguments[1] : 0;
|
||||
var to = arguments[2] !== (void 0) ? arguments[2] : null;
|
||||
return l.slice(from, to === null ? undefined : to);
|
||||
},
|
||||
|
||||
maximum: function(list, predicate) {
|
||||
if (list.length == 0) {
|
||||
return null;
|
||||
}
|
||||
var solution = null;
|
||||
var maxValue = -Infinity;
|
||||
for (var index = 0; index < list.length; index++) {
|
||||
var candidate = list[index];
|
||||
if (isBlank(candidate)) {
|
||||
continue;
|
||||
}
|
||||
var candidateValue = predicate(candidate);
|
||||
if (candidateValue > maxValue) {
|
||||
solution = candidate;
|
||||
maxValue = candidateValue;
|
||||
}
|
||||
}
|
||||
return solution;
|
||||
}
|
||||
};
|
||||
|
||||
var StringWrapper = {
|
||||
equals: function (s1, s2) {
|
||||
return s1 === s2;
|
||||
},
|
||||
|
||||
split: function(s, re) {
|
||||
return s.split(re);
|
||||
},
|
||||
|
||||
substring: function(s, start, end) {
|
||||
return s.substr(start, end);
|
||||
},
|
||||
|
||||
replaceAll: function(s, from, replace) {
|
||||
return s.replace(from, replace);
|
||||
},
|
||||
|
||||
startsWith: function(s, start) {
|
||||
return s.substr(0, start.length) === start;
|
||||
},
|
||||
|
||||
replaceAllMapped: function(s, from, cb) {
|
||||
return s.replace(from, function(matches) {
|
||||
// Remove offset & string from the result array
|
||||
matches.splice(-2, 2);
|
||||
// The callback receives match, p1, ..., pn
|
||||
return cb.apply(null, matches);
|
||||
});
|
||||
},
|
||||
|
||||
contains: function(s, substr) {
|
||||
return s.indexOf(substr) != -1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//TODO: implement?
|
||||
// I think it's too heavy to ask 1.x users to bring in Rx for the router...
|
||||
function EventEmitter() {}
|
||||
|
||||
var BaseException = Error;
|
||||
|
||||
var ObservableWrapper = {
|
||||
callNext: function(ob, val) {
|
||||
ob.fn(val);
|
||||
},
|
||||
|
||||
subscribe: function(ob, fn) {
|
||||
ob.fn = fn;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: https://github.com/angular/angular.js/blob/master/src/ng/browser.js#L227-L265
|
||||
var $__router_47_location__ = {
|
||||
Location: Location
|
||||
};
|
||||
|
||||
function Location(){}
|
||||
Location.prototype.subscribe = function () {
|
||||
//TODO: implement
|
||||
};
|
||||
Location.prototype.path = function () {
|
||||
return $location.path();
|
||||
};
|
||||
Location.prototype.go = function (url) {
|
||||
return $location.path(url);
|
||||
};
|
66
modules/angular1_router/src/module_template.js
vendored
Normal file
66
modules/angular1_router/src/module_template.js
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
angular.module('ngComponentRouter').
|
||||
value('$route', null). // can be overloaded with ngRouteShim
|
||||
factory('$router', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', routerFactory]);
|
||||
|
||||
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector) {
|
||||
|
||||
// When this file is processed, the line below is replaced with
|
||||
// the contents of `../lib/facades.es5`.
|
||||
//{{FACADES}}
|
||||
|
||||
var exports = {Injectable: function () {}};
|
||||
var require = function () {return exports;};
|
||||
|
||||
// When this file is processed, the line below is replaced with
|
||||
// the contents of the compiled TypeScript classes.
|
||||
//{{SHARED_CODE}}
|
||||
|
||||
//TODO: this is a hack to replace the exiting implementation at run-time
|
||||
exports.getCanActivateHook = function (directiveName) {
|
||||
var factory = $$directiveIntrospector.getTypeByName(directiveName);
|
||||
return factory && factory.$canActivate && function (next, prev) {
|
||||
return $injector.invoke(factory.$canActivate, null, {
|
||||
$nextInstruction: next,
|
||||
$prevInstruction: prev
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// This hack removes assertions about the type of the "component"
|
||||
// property in a route config
|
||||
exports.assertComponentExists = function () {};
|
||||
|
||||
angular.stringifyInstruction = exports.stringifyInstruction;
|
||||
|
||||
var RouteRegistry = exports.RouteRegistry;
|
||||
var RootRouter = exports.RootRouter;
|
||||
|
||||
var registry = new RouteRegistry();
|
||||
var location = new Location();
|
||||
|
||||
$$directiveIntrospector(function (name, factory) {
|
||||
if (angular.isArray(factory.$routeConfig)) {
|
||||
factory.$routeConfig.forEach(function (config) {
|
||||
registry.config(name, config);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Because Angular 1 has no notion of a root component, we use an object with unique identity
|
||||
// to represent this.
|
||||
var ROOT_COMPONENT_OBJECT = new Object();
|
||||
|
||||
var router = new RootRouter(registry, location, ROOT_COMPONENT_OBJECT);
|
||||
$rootScope.$watch(function () { return $location.path(); }, function (path) {
|
||||
if (router.lastNavigationAttempt !== path) {
|
||||
router.navigateByUrl(path);
|
||||
}
|
||||
});
|
||||
|
||||
router.subscribe(function () {
|
||||
$rootScope.$broadcast('$routeChangeSuccess', {});
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
297
modules/angular1_router/src/ng_outlet.ts
Normal file
297
modules/angular1_router/src/ng_outlet.ts
Normal file
@ -0,0 +1,297 @@
|
||||
///<reference path="../typings/angularjs/angular.d.ts"/>
|
||||
|
||||
/*
|
||||
* decorates $compileProvider so that we have access to routing metadata
|
||||
*/
|
||||
function compilerProviderDecorator($compileProvider,
|
||||
$$directiveIntrospectorProvider: DirectiveIntrospectorProvider) {
|
||||
let directive = $compileProvider.directive;
|
||||
$compileProvider.directive = function(name: string, factory: Function) {
|
||||
$$directiveIntrospectorProvider.register(name, factory);
|
||||
return directive.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* private service that holds route mappings for each controller
|
||||
*/
|
||||
class DirectiveIntrospectorProvider {
|
||||
private directiveBuffer: any[] = [];
|
||||
private directiveFactoriesByName: {[name: string]: Function} = {};
|
||||
private onDirectiveRegistered: (name: string, factory: Function) => any = null;
|
||||
|
||||
register(name: string, factory: Function) {
|
||||
if (angular.isArray(factory)) {
|
||||
factory = factory[factory.length - 1];
|
||||
}
|
||||
this.directiveFactoriesByName[name] = factory;
|
||||
if (this.onDirectiveRegistered) {
|
||||
this.onDirectiveRegistered(name, factory);
|
||||
} else {
|
||||
this.directiveBuffer.push({name: name, factory: factory});
|
||||
}
|
||||
}
|
||||
|
||||
$get() {
|
||||
let fn: any = newOnControllerRegistered => {
|
||||
this.onDirectiveRegistered = newOnControllerRegistered;
|
||||
while (this.directiveBuffer.length > 0) {
|
||||
let directive = this.directiveBuffer.pop();
|
||||
this.onDirectiveRegistered(directive.name, directive.factory);
|
||||
}
|
||||
};
|
||||
|
||||
fn.getTypeByName = name => this.directiveFactoriesByName[name];
|
||||
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name ngOutlet
|
||||
*
|
||||
* @description
|
||||
* An ngOutlet is where resolved content goes.
|
||||
*
|
||||
* ## Use
|
||||
*
|
||||
* ```html
|
||||
* <div ng-outlet="name"></div>
|
||||
* ```
|
||||
*
|
||||
* The value for the `ngOutlet` attribute is optional.
|
||||
*/
|
||||
function ngOutletDirective($animate, $q: ng.IQService, $router) {
|
||||
let rootRouter = $router;
|
||||
|
||||
return {
|
||||
restrict: 'AE',
|
||||
transclude: 'element',
|
||||
terminal: true,
|
||||
priority: 400,
|
||||
require: ['?^^ngOutlet', 'ngOutlet'],
|
||||
link: outletLink,
|
||||
controller: class {},
|
||||
controllerAs: '$$ngOutlet'
|
||||
};
|
||||
|
||||
function outletLink(scope, element, attrs, ctrls, $transclude) {
|
||||
class Outlet {
|
||||
constructor(private controller, private router) {}
|
||||
|
||||
private currentController;
|
||||
private currentInstruction;
|
||||
private currentScope;
|
||||
private currentElement;
|
||||
private previousLeaveAnimation;
|
||||
|
||||
private cleanupLastView() {
|
||||
if (this.previousLeaveAnimation) {
|
||||
$animate.cancel(this.previousLeaveAnimation);
|
||||
this.previousLeaveAnimation = null;
|
||||
}
|
||||
|
||||
if (this.currentScope) {
|
||||
this.currentScope.$destroy();
|
||||
this.currentScope = null;
|
||||
}
|
||||
if (this.currentElement) {
|
||||
this.previousLeaveAnimation = $animate.leave(this.currentElement);
|
||||
this.previousLeaveAnimation.then(() => this.previousLeaveAnimation = null);
|
||||
this.currentElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
reuse(instruction) {
|
||||
let next = $q.when(true);
|
||||
let previousInstruction = this.currentInstruction;
|
||||
this.currentInstruction = instruction;
|
||||
if (this.currentController && this.currentController.$onReuse) {
|
||||
next = $q.when(
|
||||
this.currentController.$onReuse(this.currentInstruction, previousInstruction));
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
canReuse(nextInstruction) {
|
||||
let result;
|
||||
if (!this.currentInstruction ||
|
||||
this.currentInstruction.componentType !== nextInstruction.componentType) {
|
||||
result = false;
|
||||
} else if (this.currentController && this.currentController.$canReuse) {
|
||||
result = this.currentController.$canReuse(nextInstruction, this.currentInstruction);
|
||||
} else {
|
||||
result = nextInstruction === this.currentInstruction ||
|
||||
angular.equals(nextInstruction.params, this.currentInstruction.params);
|
||||
}
|
||||
return $q.when(result);
|
||||
}
|
||||
|
||||
canDeactivate(instruction) {
|
||||
if (this.currentController && this.currentController.$canDeactivate) {
|
||||
return $q.when(
|
||||
this.currentController.$canDeactivate(instruction, this.currentInstruction));
|
||||
}
|
||||
return $q.when(true);
|
||||
}
|
||||
|
||||
deactivate(instruction) {
|
||||
if (this.currentController && this.currentController.$onDeactivate) {
|
||||
return $q.when(
|
||||
this.currentController.$onDeactivate(instruction, this.currentInstruction));
|
||||
}
|
||||
return $q.when();
|
||||
}
|
||||
|
||||
activate(instruction) {
|
||||
let previousInstruction = this.currentInstruction;
|
||||
this.currentInstruction = instruction;
|
||||
|
||||
let componentName = this.controller.$$componentName = instruction.componentType;
|
||||
|
||||
if (typeof componentName !== 'string') {
|
||||
throw new Error('Component is not a string for ' + instruction.urlPath);
|
||||
}
|
||||
|
||||
this.controller.$$routeParams = instruction.params;
|
||||
this.controller.$$template = '<div ' + dashCase(componentName) + '></div>';
|
||||
this.controller.$$router = this.router.childRouter(instruction.componentType);
|
||||
|
||||
let newScope = scope.$new();
|
||||
|
||||
let clone = $transclude(newScope, clone => {
|
||||
$animate.enter(clone, null, this.currentElement || element);
|
||||
this.cleanupLastView();
|
||||
});
|
||||
|
||||
this.currentElement = clone;
|
||||
this.currentScope = newScope;
|
||||
|
||||
// TODO: prefer the other directive retrieving the controller
|
||||
// by debug mode
|
||||
this.currentController = this.currentElement.children().eq(0).controller(componentName);
|
||||
|
||||
if (this.currentController && this.currentController.$onActivate) {
|
||||
return this.currentController.$onActivate(instruction, previousInstruction);
|
||||
}
|
||||
return $q.when();
|
||||
}
|
||||
}
|
||||
|
||||
let parentCtrl = ctrls[0], myCtrl = ctrls[1],
|
||||
router = (parentCtrl && parentCtrl.$$router) || rootRouter;
|
||||
|
||||
myCtrl.$$currentComponent = null;
|
||||
|
||||
router.registerPrimaryOutlet(new Outlet(myCtrl, router));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This directive is responsible for compiling the contents of ng-outlet
|
||||
*/
|
||||
function ngOutletFillContentDirective($compile) {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
priority: -400,
|
||||
require: 'ngOutlet',
|
||||
link: (scope, element, attrs, ctrl) => {
|
||||
let template = ctrl.$$template;
|
||||
element.html(template);
|
||||
let link = $compile(element.contents());
|
||||
link(scope);
|
||||
|
||||
// TODO: move to primary directive
|
||||
let componentInstance = scope[ctrl.$$componentName];
|
||||
if (componentInstance) {
|
||||
ctrl.$$currentComponent = componentInstance;
|
||||
|
||||
componentInstance.$router = ctrl.$$router;
|
||||
componentInstance.$routeParams = ctrl.$$routeParams;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @name ngLink
|
||||
* @description
|
||||
* Lets you link to different parts of the app, and automatically generates hrefs.
|
||||
*
|
||||
* ## Use
|
||||
* The directive uses a simple syntax: `ng-link="componentName({ param: paramValue })"`
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myApp', ['ngComponentRouter'])
|
||||
* .controller('AppController', ['$router', function($router) {
|
||||
* $router.config({ path: '/user/:id', component: 'user' });
|
||||
* this.user = { name: 'Brian', id: 123 };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ```html
|
||||
* <div ng-controller="AppController as app">
|
||||
* <a ng-link="user({id: app.user.id})">{{app.user.name}}</a>
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
function ngLinkDirective($router, $parse) {
|
||||
let rootRouter = $router;
|
||||
|
||||
return {require: '?^^ngOutlet', restrict: 'A', link: ngLinkDirectiveLinkFn};
|
||||
|
||||
function ngLinkDirectiveLinkFn(scope, element, attrs, ctrl) {
|
||||
let router = (ctrl && ctrl.$$router) || rootRouter;
|
||||
if (!router) {
|
||||
return;
|
||||
}
|
||||
|
||||
let instruction = null;
|
||||
let link = attrs.ngLink || '';
|
||||
|
||||
function getLink(params) {
|
||||
instruction = router.generate(params);
|
||||
return './' + angular.stringifyInstruction(instruction);
|
||||
}
|
||||
|
||||
let routeParamsGetter = $parse(link);
|
||||
// we can avoid adding a watcher if it's a literal
|
||||
if (routeParamsGetter.constant) {
|
||||
let params = routeParamsGetter();
|
||||
element.attr('href', getLink(params));
|
||||
} else {
|
||||
scope.$watch(() => routeParamsGetter(scope), params => element.attr('href', getLink(params)),
|
||||
true);
|
||||
}
|
||||
|
||||
element.on('click', event => {
|
||||
if (event.which !== 1 || !instruction) {
|
||||
return;
|
||||
}
|
||||
|
||||
$router.navigateByInstruction(instruction);
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dashCase(str: string): string {
|
||||
return str.replace(/[A-Z]/g, match => '-' + match.toLowerCase());
|
||||
}
|
||||
|
||||
/*
|
||||
* A module for adding new a routing system Angular 1.
|
||||
*/
|
||||
angular.module('ngComponentRouter', [])
|
||||
.directive('ngOutlet', ngOutletDirective)
|
||||
.directive('ngOutlet', ngOutletFillContentDirective)
|
||||
.directive('ngLink', ngLinkDirective);
|
||||
|
||||
/*
|
||||
* A module for inspecting controller constructors
|
||||
*/
|
||||
angular.module('ng')
|
||||
.provider('$$directiveIntrospector', DirectiveIntrospectorProvider)
|
||||
.config(compilerProviderDecorator);
|
349
modules/angular1_router/src/ng_route_shim.js
vendored
Normal file
349
modules/angular1_router/src/ng_route_shim.js
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
/** @license Copyright 2014-2015 Google, Inc. http://github.com/angular/angular/LICENSE */
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
// keep a reference to compileProvider so we can register new component-directives
|
||||
// on-the-fly based on $routeProvider configuration
|
||||
// TODO: remove this– right now you can only bootstrap one Angular app with this hack
|
||||
var $compileProvider, $q, $injector;
|
||||
|
||||
/**
|
||||
* This module loads services that mimic ngRoute's configuration, and includes
|
||||
* an anchor link directive that intercepts clicks to routing.
|
||||
*
|
||||
* This module is intended to be used as a stop-gap solution for projects upgrading from ngRoute.
|
||||
* It intentionally does not implement all features of ngRoute.
|
||||
*/
|
||||
angular.module('ngRouteShim', [])
|
||||
.provider('$route', $RouteProvider)
|
||||
.config(['$compileProvider', function (compileProvider) {
|
||||
$compileProvider = compileProvider;
|
||||
}])
|
||||
.factory('$routeParams', $routeParamsFactory)
|
||||
.directive('a', anchorLinkDirective)
|
||||
|
||||
// Connects the legacy $routeProvider config shim to Component Router's config.
|
||||
.run(['$route', '$router', function ($route, $router) {
|
||||
$route.$$subscribe(function (routeDefinition) {
|
||||
if (!angular.isArray(routeDefinition)) {
|
||||
routeDefinition = [routeDefinition];
|
||||
}
|
||||
$router.config(routeDefinition);
|
||||
});
|
||||
}]);
|
||||
|
||||
|
||||
/**
|
||||
* A shimmed out provider that provides the same API as ngRoute's $routeProvider, but uses these calls
|
||||
* to configure Component Router.
|
||||
*/
|
||||
function $RouteProvider() {
|
||||
|
||||
var routes = [];
|
||||
var subscriptionFn = null;
|
||||
|
||||
var routeMap = {};
|
||||
|
||||
// Stats for which routes are skipped
|
||||
var skipCount = 0;
|
||||
var successCount = 0;
|
||||
var allCount = 0;
|
||||
|
||||
function consoleMetrics() {
|
||||
return '(' + skipCount + ' skipped / ' + successCount + ' success / ' + allCount + ' total)';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $routeProvider#when
|
||||
*
|
||||
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
||||
* contains redundant trailing slash or is missing one, the route will still match and the
|
||||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
||||
* route definition.
|
||||
*
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
//copy original route object to preserve params inherited from proto chain
|
||||
var routeCopy = angular.copy(route);
|
||||
|
||||
allCount++;
|
||||
|
||||
if (angular.isDefined(routeCopy.reloadOnSearch)) {
|
||||
console.warn('Route for "' + path + '" uses "reloadOnSearch" which is not implemented.');
|
||||
}
|
||||
if (angular.isDefined(routeCopy.caseInsensitiveMatch)) {
|
||||
console.warn('Route for "' + path + '" uses "caseInsensitiveMatch" which is not implemented.');
|
||||
}
|
||||
|
||||
// use new wildcard format
|
||||
path = reformatWildcardParams(path);
|
||||
|
||||
if (path[path.length - 1] == '*') {
|
||||
skipCount++;
|
||||
console.warn('Route for "' + path + '" ignored because it ends with *. Skipping.', consoleMetrics());
|
||||
return this;
|
||||
}
|
||||
|
||||
if (path.indexOf('?') > -1) {
|
||||
skipCount++;
|
||||
console.warn('Route for "' + path + '" ignored because it has optional parameters. Skipping.', consoleMetrics());
|
||||
return this;
|
||||
}
|
||||
|
||||
if (typeof route.redirectTo == 'function') {
|
||||
skipCount++;
|
||||
console.warn('Route for "' + path + '" ignored because lazy redirecting to a function is not yet implemented. Skipping.', consoleMetrics());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
var routeDefinition = {
|
||||
path: path,
|
||||
data: routeCopy
|
||||
};
|
||||
|
||||
routeMap[path] = routeCopy;
|
||||
|
||||
if (route.redirectTo) {
|
||||
routeDefinition.redirectTo = route.redirectTo;
|
||||
} else {
|
||||
if (routeCopy.controller && !routeCopy.controllerAs) {
|
||||
console.warn('Route for "' + path + '" should use "controllerAs".');
|
||||
}
|
||||
|
||||
var directiveName = routeObjToRouteName(routeCopy, path);
|
||||
|
||||
if (!directiveName) {
|
||||
throw new Error('Could not determine a name for route "' + path + '".');
|
||||
}
|
||||
|
||||
routeDefinition.component = directiveName;
|
||||
routeDefinition.as = upperCase(directiveName);
|
||||
|
||||
var directiveController = routeCopy.controller;
|
||||
|
||||
var directiveDefinition = {
|
||||
scope: false,
|
||||
controller: directiveController,
|
||||
controllerAs: routeCopy.controllerAs,
|
||||
templateUrl: routeCopy.templateUrl,
|
||||
template: routeCopy.template
|
||||
};
|
||||
|
||||
var directiveFactory = function () {
|
||||
return directiveDefinition;
|
||||
};
|
||||
|
||||
// if we have route resolve options, prepare a wrapper controller
|
||||
if (directiveController && routeCopy.resolve) {
|
||||
var originalController = directiveController;
|
||||
var resolvedLocals = {};
|
||||
|
||||
directiveDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
|
||||
var locals = angular.extend({
|
||||
$scope: $scope
|
||||
}, resolvedLocals);
|
||||
|
||||
var ctrl = $injector.instantiate(originalController, locals);
|
||||
|
||||
if (routeCopy.controllerAs) {
|
||||
locals.$scope[routeCopy.controllerAs] = ctrl;
|
||||
}
|
||||
|
||||
return ctrl;
|
||||
}];
|
||||
|
||||
// we take care of controllerAs in the directive controller wrapper
|
||||
delete directiveDefinition.controllerAs;
|
||||
|
||||
// we resolve the locals in a canActivate block
|
||||
directiveFactory.$canActivate = function() {
|
||||
var locals = angular.extend({}, routeCopy.resolve);
|
||||
|
||||
angular.forEach(locals, function(value, key) {
|
||||
locals[key] = angular.isString(value) ?
|
||||
$injector.get(value) : $injector.invoke(value, null, null, key);
|
||||
});
|
||||
|
||||
return $q.all(locals).then(function (locals) {
|
||||
resolvedLocals = locals;
|
||||
}).then(function () {
|
||||
return true;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// register the dynamically created directive
|
||||
$compileProvider.directive(directiveName, directiveFactory);
|
||||
}
|
||||
if (subscriptionFn) {
|
||||
subscriptionFn(routeDefinition);
|
||||
} else {
|
||||
routes.push(routeDefinition);
|
||||
}
|
||||
successCount++;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
this.otherwise = function(params) {
|
||||
if (typeof params === 'string') {
|
||||
params = {redirectTo: params};
|
||||
}
|
||||
this.when('/*rest', params);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$q', '$injector', function (q, injector) {
|
||||
$q = q;
|
||||
$injector = injector;
|
||||
|
||||
var $route = {
|
||||
routes: routeMap,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $route#reload
|
||||
*
|
||||
* @description
|
||||
* Causes `$route` service to reload the current route even if
|
||||
* {@link ng.$location $location} hasn't changed.
|
||||
*/
|
||||
reload: function() {
|
||||
throw new Error('Not implemented: $route.reload');
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $route#updateParams
|
||||
*/
|
||||
updateParams: function(newParams) {
|
||||
throw new Error('Not implemented: $route.updateParams');
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs the given `fn` whenever new configs are added.
|
||||
* Only one subscription is allowed.
|
||||
* Passed `fn` is called synchronously.
|
||||
*/
|
||||
$$subscribe: function(fn) {
|
||||
if (subscriptionFn) {
|
||||
throw new Error('only one subscription allowed');
|
||||
}
|
||||
subscriptionFn = fn;
|
||||
subscriptionFn(routes);
|
||||
routes = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs a string with stats about many route configs were adapted, and how many were
|
||||
* dropped because they are incompatible.
|
||||
*/
|
||||
$$getStats: consoleMetrics
|
||||
};
|
||||
|
||||
return $route;
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
function $routeParamsFactory($router, $rootScope) {
|
||||
// the identity of this object cannot change
|
||||
var paramsObj = {};
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', function () {
|
||||
var newParams = $router._currentInstruction && $router._currentInstruction.component.params;
|
||||
|
||||
angular.forEach(paramsObj, function (val, name) {
|
||||
delete paramsObj[name];
|
||||
});
|
||||
angular.forEach(newParams, function (val, name) {
|
||||
paramsObj[name] = val;
|
||||
});
|
||||
});
|
||||
|
||||
return paramsObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows normal anchor links to kick off routing.
|
||||
*/
|
||||
function anchorLinkDirective($router) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function (scope, element) {
|
||||
// If the linked element is not an anchor tag anymore, do nothing
|
||||
if (element[0].nodeName.toLowerCase() !== 'a') {
|
||||
return;
|
||||
}
|
||||
|
||||
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
||||
var hrefAttrName = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
|
||||
'xlink:href' : 'href';
|
||||
|
||||
element.on('click', function (event) {
|
||||
if (event.which !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var href = element.attr(hrefAttrName);
|
||||
if (href && $router.recognize(href)) {
|
||||
$router.navigateByUrl(href);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a route object, attempts to find a unique directive name.
|
||||
*
|
||||
* @param route – route config object passed to $routeProvider.when
|
||||
* @param path – route configuration path
|
||||
* @returns {string|name} – a normalized (camelCase) directive name
|
||||
*/
|
||||
function routeObjToRouteName(route, path) {
|
||||
var name = route.controllerAs;
|
||||
|
||||
var controller = route.controller;
|
||||
if (!name && controller) {
|
||||
if (angular.isArray(controller)) {
|
||||
controller = controller[controller.length - 1];
|
||||
}
|
||||
name = controller.name;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
var segments = path.split('/');
|
||||
name = segments[segments.length - 1];
|
||||
}
|
||||
|
||||
if (name) {
|
||||
name = name + 'AutoCmp';
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function upperCase(str) {
|
||||
return str.charAt(0).toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Changes "/:foo*" to "/*foo"
|
||||
*/
|
||||
var WILDCARD_PARAM_RE = new RegExp('\\/:([a-z0-9]+)\\*', 'gi');
|
||||
function reformatWildcardParams(path) {
|
||||
return path.replace(WILDCARD_PARAM_RE, function (m, m1) {
|
||||
return '/*' + m1;
|
||||
});
|
||||
}
|
||||
|
||||
}());
|
38
modules/angular1_router/test/directive_introspector_spec.js
vendored
Normal file
38
modules/angular1_router/test/directive_introspector_spec.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
describe('$$directiveIntrospector', function () {
|
||||
|
||||
var $compileProvider;
|
||||
|
||||
beforeEach(function() {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function(_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the introspector function whenever a directive factory is registered', inject(function ($$directiveIntrospector) {
|
||||
var spy = jasmine.createSpy();
|
||||
$$directiveIntrospector(spy);
|
||||
function myDir(){}
|
||||
$compileProvider.directive('myDir', myDir);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('myDir', myDir);
|
||||
}));
|
||||
|
||||
it('should call the introspector function whenever a directive factory is registered with array annotations', inject(function ($$directiveIntrospector) {
|
||||
var spy = jasmine.createSpy();
|
||||
$$directiveIntrospector(spy);
|
||||
function myDir(){}
|
||||
$compileProvider.directive('myDir', ['foo', myDir]);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('myDir', myDir);
|
||||
}));
|
||||
|
||||
it('should retrieve a factory based on directive name', inject(function ($$directiveIntrospector) {
|
||||
function myDir(){}
|
||||
$compileProvider.directive('myDir', ['foo', myDir]);
|
||||
expect($$directiveIntrospector.getTypeByName('myDir')).toBe(myDir);
|
||||
}));
|
||||
});
|
102
modules/angular1_router/test/integration/animation_spec.js
vendored
Normal file
102
modules/angular1_router/test/integration/animation_spec.js
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
'use strict';
|
||||
|
||||
describe('ngOutlet animations', function () {
|
||||
var elt,
|
||||
$animate,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngAnimate');
|
||||
module('ngAnimateMock');
|
||||
module('ngComponentRouter');
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_) {
|
||||
$animate = _$animate_;
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerComponent('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect($animate.queue).toEqual([]);
|
||||
});
|
||||
|
||||
it('should work in a simple case', function () {
|
||||
var item;
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
// "user" component enters
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
|
||||
// navigate to pete
|
||||
$router.navigateByUrl('/user/pete');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello pete');
|
||||
|
||||
// "user pete" component enters
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('hello pete');
|
||||
|
||||
// "user brian" component leaves
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('leave');
|
||||
expect(item.element.text()).toBe('hello brian');
|
||||
});
|
||||
|
||||
|
||||
function registerComponent(name, options) {
|
||||
var controller = options.controller || function () {};
|
||||
|
||||
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
});
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
template: options.template || '',
|
||||
controllerAs: name,
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
|
||||
if (options.$canActivate) {
|
||||
factory.$canActivate = options.$canActivate;
|
||||
}
|
||||
if (options.$routeConfig) {
|
||||
factory.$routeConfig = options.$routeConfig;
|
||||
}
|
||||
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
});
|
487
modules/angular1_router/test/integration/lifecycle_hook_spec.js
vendored
Normal file
487
modules/angular1_router/test/integration/lifecycle_hook_spec.js
vendored
Normal file
@ -0,0 +1,487 @@
|
||||
'use strict';
|
||||
|
||||
describe('Navigation lifecycle', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerComponent('oneCmp', {
|
||||
template: '<div>{{oneCmp.number}}</div>',
|
||||
controller: function () {this.number = 'one'}
|
||||
});
|
||||
registerComponent('twoCmp', {
|
||||
template: '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>',
|
||||
controller: function () {this.number = 'two'}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should run the activate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('activateCmp', {
|
||||
template: '<p>hello</p>',
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should pass instruction into the activate hook of a controller', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('userCmp', {
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
|
||||
});
|
||||
|
||||
|
||||
it('should pass previous instruction into the activate hook of a controller', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
var activate = registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'oneCmp' },
|
||||
{ path: '/post/:id', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
|
||||
instructionFor('oneCmp'));
|
||||
});
|
||||
|
||||
it('should inject $scope into the controller constructor', function () {
|
||||
var injectedScope;
|
||||
registerComponent('userCmp', {
|
||||
template: '',
|
||||
controller: function ($scope) {
|
||||
injectedScope = $scope;
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(injectedScope).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
it('should run the deactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('deactivate');
|
||||
registerComponent('deactivateCmp', {
|
||||
$onDeactivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'deactivateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should pass instructions into the deactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('deactivate');
|
||||
registerComponent('deactivateCmp', {
|
||||
$onDeactivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||
{ path: '/post/:id', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||
instructionFor('deactivateCmp'));
|
||||
});
|
||||
|
||||
|
||||
it('should run the deactivate hook before the activate hook', function () {
|
||||
var log = [];
|
||||
|
||||
registerComponent('activateCmp', {
|
||||
$onActivate: function () {
|
||||
log.push('activate');
|
||||
}
|
||||
});
|
||||
|
||||
registerComponent('deactivateCmp', {
|
||||
$onDeactivate: function () {
|
||||
log.push('deactivate');
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'deactivateCmp' },
|
||||
{ path: '/b', component: 'activateCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(log).toEqual(['deactivate', 'activate']);
|
||||
});
|
||||
|
||||
it('should reuse a component when the canReuse hook returns true', function () {
|
||||
var log = [];
|
||||
var cmpInstanceCount = 0;
|
||||
|
||||
function ReuseCmp() {
|
||||
cmpInstanceCount++;
|
||||
}
|
||||
|
||||
registerComponent('reuseCmp', {
|
||||
template: 'reuse {<ng-outlet></ng-outlet>}',
|
||||
$routeConfig: [
|
||||
{path: '/a', component: 'oneCmp'},
|
||||
{path: '/b', component: 'twoCmp'}
|
||||
],
|
||||
controller: ReuseCmp,
|
||||
$canReuse: function () {
|
||||
return true;
|
||||
},
|
||||
$onReuse: function (next, prev) {
|
||||
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/on-reuse/:number/...', component: 'reuseCmp' },
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/on-reuse/1/a');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {one} }');
|
||||
|
||||
$router.navigateByUrl('/on-reuse/2/b');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {two} }');
|
||||
});
|
||||
|
||||
|
||||
it('should not reuse a component when the canReuse hook returns false', function () {
|
||||
var log = [];
|
||||
var cmpInstanceCount = 0;
|
||||
|
||||
function NeverReuseCmp() {
|
||||
cmpInstanceCount++;
|
||||
}
|
||||
registerComponent('reuseCmp', {
|
||||
template: 'reuse {<ng-outlet></ng-outlet>}',
|
||||
$routeConfig: [
|
||||
{path: '/a', component: 'oneCmp'},
|
||||
{path: '/b', component: 'twoCmp'}
|
||||
],
|
||||
controller: NeverReuseCmp,
|
||||
$canReuse: function () {
|
||||
return false;
|
||||
},
|
||||
$onReuse: function (next, prev) {
|
||||
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/never-reuse/:number/...', component: 'reuseCmp' },
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/never-reuse/1/a');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {one} }');
|
||||
|
||||
$router.navigateByUrl('/never-reuse/2/b');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(2);
|
||||
expect(elt.text()).toBe('outer { reuse {two} }');
|
||||
});
|
||||
|
||||
|
||||
// TODO: need to solve getting ahold of canActivate hook
|
||||
it('should not activate a component when canActivate returns false', function () {
|
||||
var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(false);
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('activateCmp', {
|
||||
$canActivate: canActivateSpy,
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
expect(elt.text()).toBe('outer { }');
|
||||
});
|
||||
|
||||
|
||||
it('should activate a component when canActivate returns true', function () {
|
||||
var activateSpy = jasmine.createSpy('activate');
|
||||
var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(true);
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$canActivate: canActivateSpy,
|
||||
$onActivate: activateSpy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(canActivateSpy).toHaveBeenCalled();
|
||||
expect(activateSpy).toHaveBeenCalled();
|
||||
expect(elt.text()).toBe('hi');
|
||||
});
|
||||
|
||||
|
||||
it('should activate a component when canActivate returns a resolved promise', inject(function ($q) {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$canActivate: function () {
|
||||
return $q.when(true);
|
||||
},
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(elt.text()).toBe('hi');
|
||||
}));
|
||||
|
||||
|
||||
it('should inject into the canActivate hook of controllers', inject(function ($http) {
|
||||
var spy = jasmine.createSpy('canActivate').and.returnValue(true);
|
||||
registerComponent('activateCmp', {
|
||||
$canActivate: spy
|
||||
});
|
||||
|
||||
spy.$inject = ['$nextInstruction', '$http'];
|
||||
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
var args = spy.calls.mostRecent().args;
|
||||
expect(args[0].params).toEqual({name: 'brian'});
|
||||
expect(args[1]).toBe($http);
|
||||
}));
|
||||
|
||||
|
||||
it('should not navigate when canDeactivate returns false', function () {
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$canDeactivate: function () {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
});
|
||||
|
||||
|
||||
it('should navigate when canDeactivate returns true', function () {
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$canDeactivate: function () {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { one }');
|
||||
});
|
||||
|
||||
|
||||
it('should activate a component when canActivate returns true', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$canActivate: function () {
|
||||
return true;
|
||||
},
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(elt.text()).toBe('hi');
|
||||
});
|
||||
|
||||
|
||||
it('should pass instructions into the canDeactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('canDeactivate').and.returnValue(true);
|
||||
registerComponent('deactivateCmp', {
|
||||
$canDeactivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||
{ path: '/post/:id', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||
instructionFor('deactivateCmp'));
|
||||
});
|
||||
|
||||
|
||||
function registerComponent(name, options) {
|
||||
var controller = options.controller || function () {};
|
||||
|
||||
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
});
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
template: options.template || '',
|
||||
controllerAs: name,
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
|
||||
if (options.$canActivate) {
|
||||
factory.$canActivate = options.$canActivate;
|
||||
}
|
||||
if (options.$routeConfig) {
|
||||
factory.$routeConfig = options.$routeConfig;
|
||||
}
|
||||
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
|
||||
function instructionFor(componentType) {
|
||||
return jasmine.objectContaining({componentType: componentType});
|
||||
}
|
||||
});
|
264
modules/angular1_router/test/integration/navigation_spec.js
vendored
Normal file
264
modules/angular1_router/test/integration/navigation_spec.js
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
'use strict';
|
||||
|
||||
describe('navigation', function () {
|
||||
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerComponent('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||
});
|
||||
registerComponent('oneCmp', {
|
||||
template: '<div>{{oneCmp.number}}</div>',
|
||||
controller: function () {this.number = 'one'}
|
||||
});
|
||||
registerComponent('twoCmp', {
|
||||
template: '<div>{{twoCmp.number}}</div>',
|
||||
controller: function () {this.number = 'two'}
|
||||
});
|
||||
});
|
||||
|
||||
it('should work in a simple case', function () {
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
});
|
||||
|
||||
it('should navigate between components with different parameters', function () {
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
$router.navigateByUrl('/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello igor');
|
||||
});
|
||||
|
||||
|
||||
it('should reuse a parent when navigating between child components with different parameters', function () {
|
||||
var instanceCount = 0;
|
||||
function ParentController() {
|
||||
instanceCount += 1;
|
||||
}
|
||||
registerComponent('parentCmp', {
|
||||
template: 'parent { <ng-outlet></ng-outlet> }',
|
||||
$routeConfig: [
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
],
|
||||
controller: ParentController
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/parent/...', component: 'parentCmp' }
|
||||
]);
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/parent/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(instanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('parent { hello brian }');
|
||||
|
||||
$router.navigateByUrl('/parent/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(instanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('parent { hello igor }');
|
||||
});
|
||||
|
||||
|
||||
it('should work with nested outlets', function () {
|
||||
registerComponent('childCmp', {
|
||||
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a/...', component: 'childCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$router.navigateByUrl('/a/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('outer { inner { one } }');
|
||||
});
|
||||
|
||||
|
||||
// TODO: fix this
|
||||
xit('should work with recursive nested outlets', function () {
|
||||
registerComponent('recurCmp', {
|
||||
template: '<div>recur { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
{ path: '/recur', component: 'recurCmp' },
|
||||
{ path: '/end', component: 'oneCmp' }
|
||||
]});
|
||||
|
||||
$router.config([
|
||||
{ path: '/recur', component: 'recurCmp' },
|
||||
{ path: '/', component: 'oneCmp' }
|
||||
]);
|
||||
|
||||
compile('<div>root { <div ng-outlet></div> }</div>');
|
||||
$router.navigateByUrl('/recur/recur/end');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root { one }');
|
||||
});
|
||||
|
||||
|
||||
it('should change location path', inject(function ($location) {
|
||||
$router.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
}));
|
||||
|
||||
|
||||
it('should change location to the canonical route', inject(function ($location) {
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.config([
|
||||
{ path: '/', redirectTo: '/user' },
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
}));
|
||||
|
||||
|
||||
it('should change location to the canonical route with nested components', inject(function ($location) {
|
||||
registerComponent('childRouter', {
|
||||
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
{ path: '/old-child', redirectTo: '/new-child' },
|
||||
{ path: '/new-child', component: 'oneCmp'},
|
||||
{ path: '/old-child-two', redirectTo: '/new-child-two' },
|
||||
{ path: '/new-child-two', component: 'twoCmp'}
|
||||
]
|
||||
});
|
||||
|
||||
$router.config([
|
||||
{ path: '/old-parent', redirectTo: '/new-parent' },
|
||||
{ path: '/new-parent/...', component: 'childRouter' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/old-parent/old-child');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/new-parent/new-child');
|
||||
expect(elt.text()).toBe('inner { one }');
|
||||
|
||||
$router.navigateByUrl('/old-parent/old-child-two');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/new-parent/new-child-two');
|
||||
expect(elt.text()).toBe('inner { two }');
|
||||
}));
|
||||
|
||||
|
||||
it('should navigate when the location path changes', inject(function ($location) {
|
||||
$router.config([
|
||||
{ path: '/one', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$location.path('/one');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
}));
|
||||
|
||||
|
||||
it('should expose a "navigating" property on $router', inject(function ($q) {
|
||||
var defer;
|
||||
registerComponent('pendingActivate', {
|
||||
$canActivate: function () {
|
||||
defer = $q.defer();
|
||||
return defer.promise;
|
||||
}
|
||||
});
|
||||
$router.config([
|
||||
{ path: '/pending-activate', component: 'pendingActivate' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/pending-activate');
|
||||
$rootScope.$digest();
|
||||
expect($router.navigating).toBe(true);
|
||||
defer.resolve();
|
||||
$rootScope.$digest();
|
||||
expect($router.navigating).toBe(false);
|
||||
}));
|
||||
|
||||
function registerComponent(name, options) {
|
||||
var controller = options.controller || function () {};
|
||||
|
||||
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
});
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
template: options.template || '',
|
||||
controllerAs: name,
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
|
||||
if (options.$canActivate) {
|
||||
factory.$canActivate = options.$canActivate;
|
||||
}
|
||||
if (options.$routeConfig) {
|
||||
factory.$routeConfig = options.$routeConfig;
|
||||
}
|
||||
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
});
|
182
modules/angular1_router/test/integration/shim_spec.js
vendored
Normal file
182
modules/angular1_router/test/integration/shim_spec.js
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
'use strict';
|
||||
|
||||
describe('ngRoute shim', function () {
|
||||
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider,
|
||||
$routeProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module('ngRouteShim');
|
||||
module(function (_$compileProvider_, _$routeProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
$routeProvider = _$routeProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
});
|
||||
|
||||
it('should work in a simple case', function () {
|
||||
$routeProvider.when('/', {
|
||||
controller: function OneController() {
|
||||
this.number = 'one';
|
||||
},
|
||||
controllerAs: 'oneCmp',
|
||||
template: '{{oneCmp.number}}'
|
||||
});
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
});
|
||||
|
||||
it('should adapt routes with templateUrl', inject(function ($templateCache) {
|
||||
$routeProvider.when('/', {
|
||||
controller: function OneController() {
|
||||
this.number = 'one';
|
||||
},
|
||||
controllerAs: 'oneCmp',
|
||||
templateUrl: '/foo'
|
||||
});
|
||||
|
||||
$templateCache.put('/foo', [200, '{{oneCmp.number}}', {}]);
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {one}');
|
||||
}));
|
||||
|
||||
it('should adapt routes using the "resolve" option', inject(function ($q) {
|
||||
$routeProvider.when('/', {
|
||||
controller: function TestController(resolvedService) {
|
||||
this.resolvedValue = resolvedService;
|
||||
},
|
||||
controllerAs: 'testCmp',
|
||||
resolve: {
|
||||
resolvedService: function () {
|
||||
return $q.when(42);
|
||||
}
|
||||
},
|
||||
template: 'value: {{testCmp.resolvedValue}}'
|
||||
});
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('value: 42');
|
||||
}));
|
||||
|
||||
it('should adapt routes with params', function () {
|
||||
$routeProvider.when('/user/:name', {
|
||||
controller: function UserController($routeParams) {
|
||||
this.$routeParams = $routeParams;
|
||||
},
|
||||
controllerAs: 'userCmp',
|
||||
template: 'hello {{userCmp.$routeParams.name}}'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
$router.navigateByUrl('/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello igor');
|
||||
});
|
||||
|
||||
it('should adapt routes with wildcard params', function () {
|
||||
$routeProvider.when('/home/:params*', {
|
||||
controller: function UserController($routeParams) {
|
||||
this.$routeParams = $routeParams;
|
||||
},
|
||||
controllerAs: 'homeCmp',
|
||||
template: 'rest: {{homeCmp.$routeParams.params}}'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/home/foo/bar/123');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('rest: foo/bar/123');
|
||||
});
|
||||
|
||||
it('should warn about and ignore routes with optional params', function () {
|
||||
spyOn(console, 'warn');
|
||||
$routeProvider.when('/home/:params?', {
|
||||
template: 'home'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/home/test');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {}');
|
||||
expect(console.warn)
|
||||
.toHaveBeenCalledWith('Route for "/home/:params?" ignored because it has optional parameters. Skipping.',
|
||||
'(1 skipped / 0 success / 1 total)');
|
||||
});
|
||||
|
||||
it('should adapt routes with redirects', inject(function ($location) {
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
redirectTo: '/home'
|
||||
})
|
||||
.when('/home', {
|
||||
template: 'welcome home!'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {welcome home!}');
|
||||
expect($location.path()).toBe('/home');
|
||||
}));
|
||||
|
||||
//TODO: this is broken in recognition. un-xit this when https://github.com/angular/angular/issues/4133 is fixed
|
||||
xit('should adapt "otherwise" routes', inject(function ($location) {
|
||||
$routeProvider
|
||||
.when('/home', {
|
||||
template: 'welcome home!'
|
||||
})
|
||||
.otherwise({
|
||||
redirectTo: '/home'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/somewhere');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {welcome home!}');
|
||||
expect($location.path()).toBe('/home');
|
||||
}));
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
});
|
170
modules/angular1_router/test/ng_link_spec.js
vendored
Normal file
170
modules/angular1_router/test/ng_link_spec.js
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
'use strict';
|
||||
|
||||
describe('ngLink', function () {
|
||||
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerComponent('userCmp', '<div>hello {{userCmp.$routeParams.name}}</div>', function () {});
|
||||
registerComponent('oneCmp', '<div>{{oneCmp.number}}</div>', function () {this.number = 'one'});
|
||||
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||
});
|
||||
|
||||
|
||||
it('should allow linking from the parent to the child', function () {
|
||||
$router.config([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', as: 'Two' }
|
||||
]);
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b');
|
||||
});
|
||||
|
||||
it('should allow linking from the child and the parent', function () {
|
||||
$router.config([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', as: 'Two' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b');
|
||||
});
|
||||
|
||||
|
||||
it('should allow params in routerLink directive', function () {
|
||||
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b/lol');
|
||||
});
|
||||
|
||||
|
||||
it('should update the href of links with bound params', function () {
|
||||
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: twoLinkCmp.number}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'param'});
|
||||
$router.config([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b/param');
|
||||
});
|
||||
|
||||
|
||||
it('should navigate on left-mouse click when a link url matches a route', function () {
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | one');
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./two');
|
||||
|
||||
elt.find('a')[0].click();
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | two');
|
||||
});
|
||||
|
||||
|
||||
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($router) {
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | one');
|
||||
elt.find('a').triggerHandler({ type: 'click', which: 3 });
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | one');
|
||||
}));
|
||||
|
||||
|
||||
// See https://github.com/angular/router/issues/206
|
||||
it('should not navigate a link without an href', function () {
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
expect(function () {
|
||||
compile('<a>link</a>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link');
|
||||
elt.find('a')[0].click();
|
||||
$rootScope.$digest();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
|
||||
function registerComponent(name, template, config) {
|
||||
var controller = function () {};
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
template: template,
|
||||
controllerAs: name,
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
|
||||
if (!template) {
|
||||
template = '';
|
||||
}
|
||||
if (angular.isArray(config)) {
|
||||
factory.annotations = [new angular.annotations.RouteConfig(config)];
|
||||
} else if (typeof config === 'function') {
|
||||
controller = config;
|
||||
} else if (typeof config === 'object') {
|
||||
if (config.canActivate) {
|
||||
controller.$canActivate = config.canActivate;
|
||||
}
|
||||
}
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
});
|
80
modules/angular1_router/test/util.es5.js
Normal file
80
modules/angular1_router/test/util.es5.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Helpers to keep tests DRY
|
||||
*/
|
||||
|
||||
function componentTemplatePath(name) {
|
||||
return './components/' + dashCase(name) + '/' + dashCase(name) + '.html';
|
||||
}
|
||||
|
||||
function componentControllerName(name) {
|
||||
return name[0].toUpperCase() + name.substr(1) + 'Controller';
|
||||
}
|
||||
|
||||
function dashCase(str) {
|
||||
return str.replace(/([A-Z])/g, function ($1) {
|
||||
return '-' + $1.toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function provideHelpers(fn, preInject) {
|
||||
return function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$templateCache,
|
||||
$controllerProvider;
|
||||
|
||||
module('ng');
|
||||
module('ngNewRouter');
|
||||
module(function(_$controllerProvider_) {
|
||||
$controllerProvider = _$controllerProvider_;
|
||||
});
|
||||
|
||||
inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
$templateCache = _$templateCache_;
|
||||
});
|
||||
|
||||
function registerComponent(name, template, config) {
|
||||
if (!template) {
|
||||
template = '';
|
||||
}
|
||||
var ctrl;
|
||||
if (!config) {
|
||||
ctrl = function () {};
|
||||
} else if (angular.isArray(config)) {
|
||||
ctrl = function () {};
|
||||
ctrl.$routeConfig = config;
|
||||
} else if (typeof config === 'function') {
|
||||
ctrl = config;
|
||||
} else {
|
||||
ctrl = function () {};
|
||||
ctrl.prototype = config;
|
||||
}
|
||||
$controllerProvider.register(componentControllerName(name), ctrl);
|
||||
put(name, template);
|
||||
}
|
||||
|
||||
|
||||
function put (name, template) {
|
||||
$templateCache.put(componentTemplatePath(name), [200, template, {}]);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
var elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
|
||||
fn({
|
||||
registerComponent: registerComponent,
|
||||
$router: $router,
|
||||
put: put,
|
||||
compile: compile
|
||||
})
|
||||
}
|
||||
}
|
12
modules/angular1_router/tsd.json
Normal file
12
modules/angular1_router/tsd.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "v4",
|
||||
"repo": "angular/DefinitelyTyped",
|
||||
"ref": "master",
|
||||
"path": "typings",
|
||||
"bundle": "typings/tsd.d.ts",
|
||||
"installed": {
|
||||
"angularjs/angular.d.ts": {
|
||||
"commit": "746b9a892629060bc853e792afff536e0ec4655e"
|
||||
}
|
||||
}
|
||||
}
|
@ -5,12 +5,12 @@ The sources for this package are in the main [Angular2](https://github.com/angul
|
||||
|
||||
This package contains different sources for different users:
|
||||
|
||||
1. The files located in the root folder can be consumed using CommonJS
|
||||
1. The files located in the root folder can be consumed using CommonJS.
|
||||
2. The files under `/es6` are es6 compatible files that can be transpiled to
|
||||
es5 using any transpiler. This contains:
|
||||
* `dev/`: a development version that includes runtime type assertions
|
||||
* `prod/`: a production version that does not include runtime type assertions
|
||||
3. The files under `/atscript` are the AtScript source files
|
||||
3. The files under `/ts` are the TypeScript source files.
|
||||
|
||||
As a convenience, we provide you with `/es6/{dev|prod}/es5build.js`, a script to transpile the es6 sources into es5
|
||||
using [Google Traceur](https://github.com/google/traceur-compiler/).
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user