Compare commits
1832 Commits
2.0.0-alph
...
do-not-del
Author | SHA1 | Date | |
---|---|---|---|
aac7ab7f36 | |||
5447430c3a | |||
8b52b2f415 | |||
db19e8200f | |||
4af0656dee | |||
5a61b0e995 | |||
ff6f5d1af6 | |||
2d0076195b | |||
439783d64d | |||
545636caaa | |||
272b147e44 | |||
779423c27a | |||
ec4a53e44d | |||
5ed72a8a6c | |||
3cc1a803f6 | |||
954d04a69d | |||
5af7b0cdc2 | |||
47935dd5cd | |||
b5f97b0410 | |||
fb3b490198 | |||
d4107ef2e3 | |||
29bea34169 | |||
117da538ae | |||
3b36692f26 | |||
fef78646b7 | |||
4e2bb7e2b7 | |||
811fcb3556 | |||
7a539d92ab | |||
884cf2df33 | |||
23a942ddec | |||
7c07bfff97 | |||
86ba072758 | |||
fc1e45db92 | |||
a2deafc50f | |||
2fc5c57b31 | |||
93f323cfa2 | |||
bb9dfbc578 | |||
0bb516fae2 | |||
2ffecc0e14 | |||
b9647b7347 | |||
f25c97671a | |||
0a053a4cd5 | |||
4d7d2a2daa | |||
0cf5ece7f8 | |||
66df335998 | |||
fc2fe00d16 | |||
811962b2bb | |||
b867764b0d | |||
ce08982f78 | |||
cbe0976426 | |||
515ff61fcb | |||
d7c82f5c0f | |||
566d4361e2 | |||
ea2e5521e8 | |||
eb7d8c702c | |||
5d294624fa | |||
3aaf064d11 | |||
f38a700e35 | |||
501b83441d | |||
c03e25a7b7 | |||
1f5a5895e5 | |||
8a2324f86a | |||
6335b31702 | |||
6ef7a76e39 | |||
cc79dcac7f | |||
dc6f72e963 | |||
2b313e4979 | |||
4f8f8cfc66 | |||
8b782818f5 | |||
bd510ccdbb | |||
f1ce7607a6 | |||
7dfcaac730 | |||
c7a874dd2f | |||
aa5c8ca61f | |||
5c93a8800a | |||
05bbb8efcf | |||
14a30f3ca0 | |||
5ddecb18a7 | |||
c02325dd06 | |||
4a740f23a4 | |||
a782232ca3 | |||
a29f9f3ab8 | |||
3c2b2ff332 | |||
939d318242 | |||
39a2c39cef | |||
45e8e73670 | |||
ca41b4f5ff | |||
3c561475c8 | |||
23a27776e2 | |||
01111b04ff | |||
8560e1e4bf | |||
e0fbca9fb0 | |||
a7b76826a0 | |||
ece7985b8a | |||
9883e19e2e | |||
c631cfc2fd | |||
53c99cfc95 | |||
c56f3f2246 | |||
cc0e3d2296 | |||
917d43e108 | |||
bb7d55244d | |||
8a5eb08672 | |||
477e425f57 | |||
654ff6115a | |||
628d06c17c | |||
91980382e8 | |||
2f41b5c8a0 | |||
cd8cbd3762 | |||
292ccf882a | |||
a0e13b9797 | |||
a5c0349d88 | |||
c48021ab97 | |||
beb79e75bf | |||
0b62b6f783 | |||
895c542a20 | |||
c4fd862e15 | |||
6f18bd18bb | |||
00e157dc3b | |||
4be863c223 | |||
3009be8d6e | |||
4829fbb95c | |||
40e160c22c | |||
951ecb4d90 | |||
9318033294 | |||
4648b3e5de | |||
740e80492d | |||
cc22b051e3 | |||
75405ae0f5 | |||
f12d51992d | |||
6fd5bc075d | |||
675e582ffd | |||
0a22e8eefd | |||
44a4814766 | |||
3c23238129 | |||
916ed55d82 | |||
2451eb9ded | |||
ed639784d4 | |||
b77a3794b8 | |||
73a9ee4a05 | |||
9adf80385b | |||
a86c554a8e | |||
62078eee45 | |||
24e280a21a | |||
f7ff6c5a12 | |||
f6a7d6504c | |||
96bf42261b | |||
72bb38f83b | |||
4c9900dc3a | |||
6b26102931 | |||
bec5c5fdad | |||
04c11bb749 | |||
3f5331be9d | |||
48751cceae | |||
4a9745ef78 | |||
acc0fe6cf9 | |||
b238414984 | |||
231ed69507 | |||
60b10134df | |||
73c0a9daaf | |||
398bbb6aa9 | |||
05d1312306 | |||
bc6d1c87a6 | |||
712c7d5c3b | |||
e9479b30e8 | |||
7f6685e451 | |||
ce4eae65a7 | |||
4df48b202c | |||
33ced7088f | |||
3329977ec9 | |||
f84c3fdc5f | |||
44e1b23813 | |||
12b0a3d0e5 | |||
d2825077b1 | |||
156a52e390 | |||
bb7221f922 | |||
2eb4ee8393 | |||
87fe47737a | |||
9317056138 | |||
a235ae16ed | |||
79afcf0766 | |||
161a4dd15f | |||
6580d67875 | |||
04c6b2fe85 | |||
203b2ba637 | |||
5b99b8c18a | |||
6a011f4e8e | |||
f0c1f9ef39 | |||
97f35714f7 | |||
00e5b7d30a | |||
39c0f9ebb3 | |||
e60c765280 | |||
91dd672aa4 | |||
a5d2aadecb | |||
cc6749c158 | |||
f48142e679 | |||
947f9c3f56 | |||
7cd4741fcb | |||
2d520ae7e7 | |||
5d59c6e80f | |||
50345b8c36 | |||
7606c96c80 | |||
3466232f8b | |||
6e842fc5bf | |||
50c795280f | |||
51b22cf14f | |||
2291929a15 | |||
7fac4efede | |||
b96869afd2 | |||
6f4ee6101c | |||
6db27153ef | |||
9a11ec2624 | |||
aff1bc9f2d | |||
43512aa5eb | |||
c7f3aa71fb | |||
f9da3c98d6 | |||
a20a420be6 | |||
beadf6167a | |||
c8da7e995f | |||
8be6b12c2b | |||
4728f29f67 | |||
2a942e49ac | |||
e3aa19049f | |||
ef1eadadcd | |||
f444c11d21 | |||
d75502eeee | |||
ebcd14f8e9 | |||
b65f66feff | |||
dd68ae3ef1 | |||
1b04d70626 | |||
01bca41168 | |||
94e1ab33ce | |||
0b08dd8674 | |||
b2b47177cd | |||
f08257ff4a | |||
d1f4222c83 | |||
d21331e902 | |||
37f138e83d | |||
5dab0bad3c | |||
85e70a4cde | |||
6b564ecda5 | |||
d4cceff0ef | |||
a415613457 | |||
1a41bd1ca4 | |||
5a99393355 | |||
afcb3c0035 | |||
d2d36c61f3 | |||
0bd97ecda2 | |||
46bbcefb36 | |||
74b57dfa7d | |||
8c9c0986e9 | |||
4028fcaa51 | |||
7a8ef1eae5 | |||
e811a5d97f | |||
df44e3e425 | |||
cdb1a237e5 | |||
fcafdff10b | |||
c586656d43 | |||
3a307c2794 | |||
4f17dbc721 | |||
99989f5d3f | |||
6baf3baedd | |||
0d1f3c3b07 | |||
83e2d3d1cb | |||
b4613ab2d2 | |||
0ca05eee45 | |||
26c9e1dc70 | |||
797cb5ae7b | |||
63b82cd730 | |||
2b704f0586 | |||
ce5ba80792 | |||
8b18ef4ba2 | |||
cd18de7a21 | |||
fd19671c07 | |||
3fcd6fd93f | |||
c8d53d71a3 | |||
790362e243 | |||
2eda7a5293 | |||
9925aa89dc | |||
6195a45ae2 | |||
422d380b3e | |||
550ab31bd0 | |||
5fceb21549 | |||
29caa37943 | |||
8efbcc996a | |||
91c64d2b8d | |||
82e7ecd611 | |||
3d53b33391 | |||
34624b2db2 | |||
1ab5eb0844 | |||
630028350a | |||
7e4fd7d7da | |||
af2e80e068 | |||
8e6091de6c | |||
ecdaded25f | |||
c161ed415d | |||
ff3b71f7b3 | |||
16cc9b46aa | |||
3ce11ed58c | |||
7db75fa361 | |||
d6d4568830 | |||
a55d796c4b | |||
73f02c7861 | |||
8c8754e573 | |||
c977a906b3 | |||
e0eea6c2f4 | |||
3e377f520e | |||
8dc82a0080 | |||
4624a35845 | |||
8d4499959a | |||
2dfc9c653b | |||
106db0aba8 | |||
28c4852cd6 | |||
13c8211065 | |||
e73d0511cf | |||
e18626b7a2 | |||
4df7b1cfbc | |||
f9573ece41 | |||
93ade740e2 | |||
a46437c57d | |||
633c7d1ebe | |||
251953218c | |||
0d6cc17252 | |||
c8989c900f | |||
6134320f16 | |||
7f647822bd | |||
e34a04d2ad | |||
44093905e2 | |||
3e2900f74b | |||
11fd2eccec | |||
0eee1d5de3 | |||
1b77604ee2 | |||
cc5cfe87c3 | |||
48f230a951 | |||
2be50bdbb0 | |||
f7258ea52a | |||
28e8b2faab | |||
3c3e9ddb10 | |||
5162fb6d52 | |||
2fdb39e60a | |||
43c71ae103 | |||
f0bd528d77 | |||
50a024b42f | |||
b48f7bcb8d | |||
763ca60f5b | |||
0eca7abdd8 | |||
d0a95e35af | |||
acc6c8d0b7 | |||
3dbc66c1ac | |||
4ad6bcce54 | |||
0988cc82b0 | |||
20b03bad11 | |||
81d27daf0d | |||
bb8b82b3f5 | |||
915a6666f8 | |||
72da547d6a | |||
7c76a75452 | |||
a32c4ad2f0 | |||
fb3608aa5d | |||
0a46f37444 | |||
9b39e499ac | |||
a67cc8229d | |||
b58e9ea775 | |||
422effdd18 | |||
3b690b68a6 | |||
43349dd373 | |||
e44e8668ea | |||
69e72c0786 | |||
553344739c | |||
367f0fd142 | |||
58d9e7fc5a | |||
9d9e9c6ff1 | |||
ba88db5141 | |||
62e7c0f464 | |||
fc83bbbe98 | |||
482c019199 | |||
b449467940 | |||
0aba42ae5b | |||
0d1bf8148b | |||
b42411ba1f | |||
5a21f168d6 | |||
00b726f695 | |||
f02da4e91a | |||
d6b65db9a7 | |||
6f4e49ed53 | |||
46b212706b | |||
ca16fc29a6 | |||
9edea0b139 | |||
d15a1d64e1 | |||
c87847974a | |||
6f68330fa5 | |||
2b63330a36 | |||
06e4ca4bb3 | |||
43437c175a | |||
8d90a5a4cf | |||
93a4ca652a | |||
41178367d1 | |||
54f2edbb90 | |||
b652a7fc9f | |||
e34eb4520f | |||
190bcc89c1 | |||
64fc4648b7 | |||
bdb59129d0 | |||
d455942389 | |||
cdb3678fe3 | |||
e73ac1e992 | |||
51f3d22e4f | |||
00aa7a76b6 | |||
27b87ef535 | |||
44709e0dca | |||
31a7709ece | |||
a441b5b8fe | |||
76b8a49bfb | |||
db54a84d14 | |||
eb6ff65af7 | |||
23ee29b6a2 | |||
73a69895d8 | |||
2799e7a3ca | |||
b43f95435b | |||
450f61d384 | |||
f3dd91e1d7 | |||
979946c062 | |||
51e661eb74 | |||
921a17960c | |||
7a4f6621ed | |||
83bc5c97ef | |||
3f08efa35d | |||
0914dc35e8 | |||
b6746cce9c | |||
8cd97c2054 | |||
32d8cde9c6 | |||
1803ed2512 | |||
f08060b0b0 | |||
b77a4a40a4 | |||
e1109d52e1 | |||
0668ba50e8 | |||
44ff005ce3 | |||
aa88438b54 | |||
85be729c70 | |||
a5dc5705a3 | |||
9e3d13f61f | |||
961c9d48ae | |||
9229bbbc80 | |||
34feecf60e | |||
4c762a6be3 | |||
0426325ef7 | |||
0b54e3cf0a | |||
5cf58971f1 | |||
6518ff88b2 | |||
42b0c1d8a2 | |||
4a965052f9 | |||
5725c5925c | |||
a46291b67c | |||
4ac76ca281 | |||
e7a8e2757b | |||
1266460386 | |||
0ccb6e0dfc | |||
3050ae155c | |||
402fd934d0 | |||
6c86e8d80a | |||
60e6f91a53 | |||
e676fded21 | |||
25e070dd65 | |||
da8eb9f8b8 | |||
806a25413c | |||
5af1e891cd | |||
79eda30f0f | |||
6d02d2f107 | |||
ded518d47f | |||
27436270fd | |||
b4ea0b1601 | |||
7b31178546 | |||
2ff83324af | |||
4ef86891a3 | |||
eb5763c23f | |||
d1a3e3aff1 | |||
93d0a01d3d | |||
9af2d8b810 | |||
94dc632a6d | |||
29231877e6 | |||
e68252a79b | |||
4ec2a30942 | |||
e6b24437a9 | |||
a05f7b2d76 | |||
61e18434d3 | |||
57473e72ec | |||
c3bdd504d0 | |||
daa9da4047 | |||
245b0910ed | |||
a77db44129 | |||
34b3c534e7 | |||
fa47890032 | |||
d84a43c828 | |||
9a1babb30c | |||
30a332ee36 | |||
426b002897 | |||
2de8364de2 | |||
eacc9e6541 | |||
749dec7dfb | |||
96a9e66616 | |||
46e105f3ab | |||
f7a0e9ecb6 | |||
93025d1bc6 | |||
98d49d4ce3 | |||
1426f680f5 | |||
c7fc51a185 | |||
b7e69bc1a1 | |||
72544ba551 | |||
c43dd5a655 | |||
7f4954bed6 | |||
f1fc1dc669 | |||
cbe85a0893 | |||
7073cf74fe | |||
9d265b6f61 | |||
776a83f9da | |||
f29457f3f0 | |||
a005d1595e | |||
8d746e3f67 | |||
37e6da6dfb | |||
8aa2a0c1b2 | |||
6bfd514caf | |||
ad3f18c0dd | |||
39d04b4a15 | |||
6fbe56dbf2 | |||
8ebb8e44c8 | |||
6fcf962fb5 | |||
2708ce6a17 | |||
30bec78da3 | |||
9a04fcd061 | |||
ae62f082fd | |||
9cc3b2ca9e | |||
3fe1cb0253 | |||
0ed7773223 | |||
3f55aa609f | |||
74b45dfbf8 | |||
5c9f871b21 | |||
77dc6ef411 | |||
5eca6e4e40 | |||
0c65d5cf2b | |||
f65ebec3ed | |||
81bf3f66ca | |||
3cbded6694 | |||
137fff9632 | |||
695c08b9dd | |||
119794249b | |||
afb72164e4 | |||
9fee5630fd | |||
01de58d650 | |||
dabf214f17 | |||
fb2539e1d5 | |||
ad9f02a73e | |||
2d73583253 | |||
73f017bad9 | |||
055282f156 | |||
fe7de53b89 | |||
17e4cfc748 | |||
1608d91728 | |||
a3b90411aa | |||
5781b96490 | |||
f208ee0d57 | |||
8aa388de6c | |||
51d4c9dcbd | |||
e81dea695c | |||
3fec27961e | |||
3784696b9e | |||
8c45aebc18 | |||
810c722413 | |||
e2116c53f3 | |||
296a447e3c | |||
0961bd1eff | |||
9340e1b065 | |||
ae4fa56ee9 | |||
2d9d7f1310 | |||
5ee84fe0f6 | |||
1620426393 | |||
bf598d6b8b | |||
24eb8389d2 | |||
fcfddbf79c | |||
dc64e90ab9 | |||
e12b1277df | |||
797914e948 | |||
e0b0a594bb | |||
ed0ade6f34 | |||
5cc7b41f39 | |||
f2f1ec0117 | |||
e913d9954d | |||
d20488752b | |||
855f3afb28 | |||
3f44377f2f | |||
90295e3252 | |||
a620f95891 | |||
db66509e66 | |||
6605eb30e9 | |||
3644eef860 | |||
fb2509675d | |||
eef9512ce6 | |||
9f00a1b902 | |||
c369bc747d | |||
c03e1f2f59 | |||
17dcbf66b9 | |||
40b907a657 | |||
a33195dcf3 | |||
c693c03f1d | |||
de127109f9 | |||
83208983b3 | |||
327d04c9c6 | |||
54edce2bab | |||
1a145ac500 | |||
9f978cf49d | |||
41b781107b | |||
dcf75126bf | |||
1143b0389a | |||
97a2119596 | |||
fbd2dd9ca2 | |||
f463e09b9f | |||
42a5b6cbda | |||
0ad1215a92 | |||
7733c97df3 | |||
8a9e9c7bd3 | |||
3d8eb8cbca | |||
894747c34c | |||
8d5a312585 | |||
8eb81b3741 | |||
22d8f73bc9 | |||
249a6bdd98 | |||
3ad81b1beb | |||
5ab0534164 | |||
5150344213 | |||
98cef76931 | |||
6c5b653593 | |||
9ed8f2d26e | |||
33a2f86b28 | |||
fed1672a43 | |||
54dbed4f48 | |||
df759b8d4b | |||
6edf0474cc | |||
826f89f862 | |||
c43aec2182 | |||
ae75e3640a | |||
a5f2cc73f6 | |||
e1e5c40ef7 | |||
6420f75320 | |||
5face35ae5 | |||
398060d5ff | |||
638fd744aa | |||
098b461b69 | |||
9decc3d823 | |||
a5f2e205ef | |||
8899b83927 | |||
f6a410a4a8 | |||
3d5bb23184 | |||
ef37d2ae0b | |||
2eb234bc63 | |||
758ee95880 | |||
40e1112a8e | |||
397f5e2390 | |||
1a212259af | |||
f114dd300b | |||
5954a26bce | |||
bdbbe5aa20 | |||
15911367a2 | |||
8dd3f59c81 | |||
c9d28492b7 | |||
d1f93072a8 | |||
92d8bf9619 | |||
bd2281e32d | |||
0c50bc6449 | |||
f164715678 | |||
2aa615b4ae | |||
42c89b1b9b | |||
f6b75f56ad | |||
280540e4a2 | |||
fea216db12 | |||
b260eb06f6 | |||
1c937a10f9 | |||
ca23b4c55f | |||
7e12208ca6 | |||
2773281338 | |||
f8e8d22e4e | |||
cf4a9236b9 | |||
4450e7b246 | |||
cdbf67ee05 | |||
9a67f38728 | |||
25560ed048 | |||
2aa19fd078 | |||
cca9a58ded | |||
d6a25325c7 | |||
a717da2d3e | |||
b3e801ed9e | |||
3683fb6886 | |||
3bd0ce291e | |||
523fc5536c | |||
f5efccfb44 | |||
b6ec22de6b | |||
15f27b5455 | |||
127401598b | |||
503b07f698 | |||
f0a6329005 | |||
2982892acc | |||
9de56481f1 | |||
777eb2f159 | |||
05eebe0fed | |||
fdfbbd5bac | |||
1f3f8ef6c8 | |||
820eeb49d1 | |||
2d4be1c9eb | |||
2fef30f619 | |||
10113b63b5 | |||
545caab433 | |||
3f90659cc1 | |||
131914ac94 | |||
29a7c4538c | |||
f195bb608c | |||
66caabca0c | |||
25c6a3715d | |||
97cf0e40d5 | |||
6988a550ea | |||
8a1cdc2dd5 | |||
dadd5ddded | |||
56f8c95ee9 | |||
ed50e17e5b | |||
33b518ad21 | |||
b0e7c14545 | |||
5742d4720a | |||
9b356d9b86 | |||
793ac3f6b4 | |||
9b094e42a3 | |||
6ce7a5a1ea | |||
88920bfee1 | |||
2717bcc3af | |||
5d386dc426 | |||
f34af4f249 | |||
f04b6978fb | |||
ab958598d7 | |||
1914847e72 | |||
d95f0fd83d | |||
243612e36d | |||
c5cca8e098 | |||
99f7404d8b | |||
9ff6b0828f | |||
6f052d1daf | |||
63c194b71f | |||
46911117f1 | |||
2de1030413 | |||
1f6ade894e | |||
8407cfeac7 | |||
91d64a2855 | |||
40a06af79b | |||
8aef86f4a0 | |||
5bdc6ecec8 | |||
c179b5033b | |||
86f47273bc | |||
2e1bd46bb1 | |||
a9e773b47b | |||
10d38cbb72 | |||
a5371bfb8a | |||
4b2740f270 | |||
5b371736b2 | |||
c9b4bcf689 | |||
013f9a2bbc | |||
5bf1c93ead | |||
4f6ec01932 | |||
0f79e504c9 | |||
1a4e911b8b | |||
1f98519380 | |||
aad7010952 | |||
1be9ea681b | |||
f259a2204b | |||
4b1db0e61c | |||
aee764d14d | |||
47585498af | |||
37c5320e33 | |||
01111a1122 | |||
0b2bb1b6f5 | |||
f57df3cf8a | |||
c9c81e1fbc | |||
e38e04c1c2 | |||
99587ea4ed | |||
58b18d7fe7 | |||
04a50f5832 | |||
e157a065b0 | |||
41ef4b3d4a | |||
262650ab39 | |||
8c076d5a73 | |||
c5c456120c | |||
fdf6bc18dd | |||
297f0fd2c3 | |||
86405345b7 | |||
12c49042ab | |||
ba46ca683b | |||
ca42b49fa2 | |||
1b28cf71f5 | |||
6fd52dfb38 | |||
af2f5c3d7d | |||
65be81baf8 | |||
7a3689f175 | |||
8675b8dc48 | |||
279e816ea7 | |||
2d60ff14ae | |||
dee1b774f2 | |||
c0f2a22a08 | |||
5c8d3154d7 | |||
40f8a45b95 | |||
76a418760e | |||
49bf3f5b3a | |||
773c34900f | |||
791153c93c | |||
721f53f0d6 | |||
7498050421 | |||
5e3ccbcea9 | |||
8879aa1df4 | |||
44e0ad4987 | |||
c449f325ba | |||
5fe60759f9 | |||
6c389ed32f | |||
45549cda61 | |||
a13052fc73 | |||
4e7bb03e81 | |||
935c39a7e2 | |||
e0c1c13004 | |||
c08ca22dba | |||
a3f6e19881 | |||
37b617dccf | |||
c60ef45bc8 | |||
2b1ac63e3a | |||
6686bc62f6 | |||
1eaa193c51 | |||
b620f4f456 | |||
e484c62a8d | |||
9ba400d7d5 | |||
9cbd8f7afc | |||
8e6e90e703 | |||
26676c4833 | |||
39e0b4903c | |||
54c577cfe0 | |||
27024915e4 | |||
80f3e7591e | |||
933f45ef31 | |||
4fc37aeabd | |||
2fd1e88199 | |||
17f317d31e | |||
5941c92a31 | |||
d44d0852e5 | |||
80deac5cde | |||
8a54c1a115 | |||
0dbff55bc6 | |||
22916bb5d1 | |||
fe01e2efb7 | |||
7afee97d1b | |||
6fc267f22c | |||
ac84468f1c | |||
387a90e546 | |||
6eeb9495d8 | |||
566b4ef481 | |||
a191e9697c | |||
5504ca1e38 | |||
5c0cfdee48 | |||
bc888bf3a1 | |||
1745366530 | |||
1fb0db4aeb | |||
61960c51a3 | |||
14a3ade662 | |||
de97687422 | |||
a0251305ea | |||
2b8d12ddf0 | |||
1f6fd3c8fc | |||
55860e1621 | |||
4d51158b1a | |||
7d9c1e1225 | |||
d53edfec47 | |||
a6e5ddc5af | |||
b866f32832 | |||
97833d48c1 | |||
9c0031f7a5 | |||
164a091c71 | |||
4ed6cf7519 | |||
5267115481 | |||
43148d8233 | |||
537e99b4ea | |||
e1fcab777c | |||
f39c9c9e75 | |||
bbed364e7b | |||
3aca5ff9e2 | |||
9146bb0816 | |||
0c9e8dbf60 | |||
b34a04d53a | |||
0658eb4429 | |||
e178ee4ba0 | |||
9f506cd330 | |||
729dc3b764 | |||
7ce0fc7d47 | |||
b60eecfc47 | |||
346304762e | |||
e213939f28 | |||
4c39eace52 | |||
86fbd50c3d | |||
87d824e1b4 | |||
50acb96130 | |||
29c2dcff61 | |||
cea103a7ff | |||
d18694a1c3 | |||
b746c64229 | |||
d38aa5e25f | |||
efdc2d5118 | |||
515a8e0765 | |||
45de65bd45 | |||
9d6b98794e | |||
7aa1790874 | |||
5cd490eba2 | |||
ac1156739d | |||
7cefec77ef | |||
36d25f2a07 | |||
c3d2459a4e | |||
8847580fd7 | |||
cf3548a02f | |||
c197e2bb42 | |||
d1c989b8a5 | |||
57c9a07fff | |||
a19c4e8f9a | |||
53083c0b52 | |||
994d9212c1 | |||
52ddc96c9f | |||
057abefe50 | |||
f0e24b1a1e | |||
a1e3004e62 | |||
e504d4eb05 | |||
a6ad61d83e | |||
27a47e7841 | |||
b00b9fe564 | |||
fa0718ba9a | |||
155b88213c | |||
35ea02fb81 | |||
ddd2ac4f55 | |||
6f281ab3c4 | |||
fe8a7b0e82 | |||
76e6214b9b | |||
2d8f776e38 | |||
cf2d3cf920 | |||
b160ada5d1 | |||
1090601e8b | |||
01dd7dde24 | |||
13c39a52c6 | |||
3b80ab51ba | |||
1a386a58c8 | |||
04220be8fd | |||
48bf349c3c | |||
21fc1bb655 | |||
d38d375fa6 | |||
602836800b | |||
2953ea10a7 | |||
a738d0d54d | |||
d781e69948 | |||
c9b71fb5e2 | |||
dd6cb233b5 | |||
a3cf58b67a | |||
70d944a59c | |||
a5a422f8e7 | |||
e93b3d2360 | |||
7bc2d9a93a | |||
1c929031a2 | |||
f2809d1ed8 | |||
f4f6b8721a | |||
7e352a27f7 | |||
0c6b16c208 | |||
10475b859d | |||
4d793c4eb8 | |||
3ae29c08ac | |||
3331321f64 | |||
c6064a30a1 | |||
040b101842 | |||
a78a43c816 | |||
ec198b0dc6 | |||
b5d14c26d2 | |||
2019050db2 | |||
5f999225ba | |||
307d105d2c | |||
0b6967fd74 | |||
ef0c32512c | |||
9096481744 | |||
5936624d11 | |||
83723671af | |||
4d825dd9fd | |||
8e38291156 | |||
f93512bf27 | |||
c3fafa0651 | |||
9036f78b74 | |||
263122ea5f | |||
324f0147f6 | |||
be48ba1b06 | |||
b2a7fd05cb | |||
84f859d7b2 | |||
cbc8d0adf8 | |||
420e83a396 | |||
89f61087c7 | |||
1dcb663917 | |||
b7b56785d1 | |||
3a62023260 | |||
fa2ce8100b | |||
172a5663ef | |||
f4b972815b | |||
0cb93a436d | |||
b2e804c961 | |||
85ce184197 | |||
29c77df4be | |||
c39e0463a3 | |||
c27bc1956b | |||
72707d80ab | |||
f18356307b | |||
00475f25c8 | |||
bab6023eee | |||
5e12a95789 | |||
e5904f4089 | |||
cf1122cf9e | |||
6da79673c2 | |||
352ee53202 | |||
a20639558b | |||
b9347eb01c | |||
4dbd8ed6b8 | |||
cb980d3e43 | |||
f154e2c6cf | |||
d0a64f9c86 | |||
16ef21d086 | |||
39ecd01b86 | |||
5e0f8cf3f0 | |||
6c6b316bd9 | |||
b49dac7be5 | |||
4c26397937 | |||
e26e4f922e | |||
2ab1085dfb | |||
acc3a2de83 | |||
12abdd8782 | |||
2bcdec5aaf | |||
16dfe3c63f | |||
b4a467e387 | |||
53628e19ac | |||
62dd3ceb64 | |||
d5066a9a0f | |||
32b37432b0 | |||
5f3d02bc7c | |||
97e94dd6e0 | |||
846c031ec9 | |||
92340350d2 | |||
d4827caa08 | |||
6ce13b68fa | |||
e82b700ad0 | |||
60a2ba87d4 | |||
83c19a1fbc | |||
1513e201bb | |||
a38c9a1ef7 | |||
d5f5ce82ca | |||
6dc88f5b61 | |||
7a2ce7ff21 | |||
1ac38bd69a | |||
982fad0c45 | |||
390cefac72 | |||
798bface7f | |||
276fec6e50 | |||
95af14b97c | |||
0f0a8ade7c | |||
e0c83f669e | |||
016f0d8e9e | |||
3e5716ec16 | |||
75e6dfb9ab | |||
9634e8d14a | |||
f95a604b59 | |||
3ff20cd7e3 | |||
17f73cb7bc | |||
fba0e2ff12 | |||
b62415c962 | |||
adc135e6c8 | |||
ceac045a7f | |||
4d6da7b1a7 | |||
01b9de7a15 | |||
0a872ffd38 | |||
6f3a6a55a0 | |||
0795dd307b | |||
49fb7ef421 | |||
cbeeff2bd6 | |||
ce013a3dd9 | |||
1f7449ccf4 | |||
c43636f2bb | |||
9c2fe660a3 | |||
abc266fa35 | |||
7d853dd9ad | |||
3ac2821a1b | |||
666dc75c15 | |||
e9332c66d2 | |||
830aecd1a7 | |||
ac6959c819 | |||
2bf21e1747 | |||
9105ab9596 | |||
165357bfa3 | |||
33c7f74cb9 | |||
cb84cbf545 | |||
b2e0946696 | |||
67c80fbb5e | |||
c574e6808f | |||
9175a049d3 | |||
0035575c82 | |||
7bfe8aa553 | |||
1bff47f97d | |||
e8e61de28d | |||
54f8308999 | |||
6c99746f0b | |||
4086b49046 | |||
0fad9c2786 | |||
d75f928fca | |||
68f9aaf214 | |||
efe4633b15 | |||
19e65382f7 | |||
5a9eda73b4 | |||
c6f2b3e96b | |||
55921be1af | |||
9b1b5b393d | |||
3857c8226e | |||
595bcdd1ac | |||
27c25bd0e8 | |||
aec95015f8 | |||
b2db6401cc | |||
ebe14720eb | |||
7112d008d0 | |||
90af4763d8 | |||
390046d7b3 | |||
587c119c75 | |||
3019140e7e | |||
7a80f0d1e1 | |||
0894318f50 | |||
f9fc524a74 | |||
9019c6f937 | |||
166b73f4f3 | |||
cb94111f18 | |||
a88b887a05 | |||
6e62217b78 | |||
a01a54c180 | |||
db8290632f | |||
7bb5167239 | |||
6cdc53c497 | |||
15ae710d22 | |||
dd50124254 | |||
ff36b0384a | |||
50c9bed630 | |||
8b1b427195 | |||
9a05ca95f6 | |||
05266241af | |||
4ddf5536b4 | |||
f389b5a961 | |||
bac1a6eab3 | |||
ff400726ca | |||
267d864976 | |||
97a1084c99 | |||
61b339678d | |||
d537a26297 | |||
d414734aac | |||
817ddfa847 | |||
c1154b30c7 | |||
0d71345b93 | |||
f235454dd6 | |||
6a80578d05 | |||
d33cd43db1 | |||
9e3df8eefe | |||
cf73ad7c8f | |||
3e68b7eb1f | |||
9fbafba993 | |||
7a524e3deb | |||
7b6c4d5acc | |||
99c0d503d7 | |||
f86edae9f3 | |||
df1b1f6957 | |||
9099160038 | |||
119abe7bb9 | |||
67ed2e2c0a | |||
6d36a7a45f | |||
e2b1e1577d | |||
b30ddfbfc5 | |||
abfb522f83 | |||
b8136cc26e | |||
d00b26d941 | |||
12637a761c | |||
1a0aea67a0 | |||
0f1465b899 | |||
c0cfd3c6ed | |||
a81923b793 | |||
7150ace7c7 | |||
bdce154282 | |||
5a84048f72 | |||
188bda813e | |||
29700aa188 | |||
3229bf1665 | |||
52595f52f9 | |||
edec158dd8 | |||
0297398f5e | |||
9485f5a813 | |||
8f8c017882 | |||
eba6e7946d | |||
3cfe281790 | |||
6eac4b68bc | |||
fcbfacb6d5 | |||
b600915953 | |||
905f38acb8 | |||
38f4c5f155 | |||
509f4ec611 | |||
27a7b51d99 | |||
a033f8335b | |||
b98c9e74e1 | |||
9f784dcc5a | |||
b625f2471a | |||
3aa2606ff1 | |||
89704e0f93 | |||
908a102a87 | |||
dd6e0cf1b5 | |||
52a6ba7ed9 | |||
43e0fa513b | |||
de978229b2 | |||
ba62fe974b | |||
3a40cb1a85 | |||
883e0c48b1 | |||
18b6a55764 | |||
00d3b6083c | |||
c386fc8379 | |||
43527172ed | |||
b88384eed8 | |||
107016ec12 | |||
d930ad1816 | |||
072446aed3 | |||
2e1f3f003d | |||
fdd8bd1a36 | |||
7db911fdd4 | |||
b6fd81169b | |||
3ae856ab8b | |||
ce5b37239e | |||
3e17c99f4e | |||
bb8976608d | |||
cd52318f48 | |||
2570b72158 | |||
6e79de794c | |||
c4be30d2e8 | |||
57240c85a5 | |||
a66cdb469f | |||
505da6c0a8 | |||
4fe0f1fa65 | |||
ec4ca0eace | |||
db95fd6ca9 | |||
ca13f1c024 | |||
277b1fc473 | |||
8836219b16 | |||
6f5e3f9390 | |||
a84c2d7fee | |||
f114d6c560 | |||
163d80adb7 | |||
9e05814212 | |||
ab56be46e1 | |||
6a0cbb8a57 | |||
4e2c68354e | |||
62a0809e81 | |||
76d6f5fa0d | |||
0f1b370117 | |||
e589f9949b | |||
0f774df811 | |||
351f24e8eb | |||
aecb60a604 | |||
4d691b61ee | |||
11955f9b13 | |||
5ff31f0713 | |||
deba804671 | |||
e5b87e55da | |||
d097784d57 | |||
15f6b27ae0 | |||
176e55927c | |||
365be6a309 | |||
713e6d4aff | |||
a8e277b067 | |||
3aa322a9c6 | |||
a02614beaa | |||
d2527b504a | |||
46cd868827 | |||
b1a9e445b3 | |||
5297c9d9cc | |||
ee7caceec7 | |||
a0b5964a63 | |||
cacdead96d | |||
96ae348648 | |||
78946fe9fa | |||
33e53c9a59 | |||
c493d88405 | |||
8bf6ef6544 | |||
ca40ef5ac7 | |||
7c0d4976b1 | |||
30de2db349 | |||
602641dffd | |||
560cc14d97 | |||
de56dd5f30 | |||
fa5bfe4b64 | |||
446657bdd5 | |||
79830f1c75 | |||
6e1fed42b7 | |||
d35c109cb9 | |||
fad3b6434c | |||
073ec0a7eb | |||
70b23ae2ca | |||
769835e53e | |||
ac55e1e27b | |||
0700c8a252 | |||
c79e657fcd | |||
d9648887b8 | |||
35cd0ded22 | |||
d2efac18ed | |||
ff2ae7a2e1 | |||
e1058a4d8a | |||
d327ac4b43 | |||
1ad2a02b11 | |||
1e8864c4a5 | |||
c209836fd0 | |||
7d1b6af073 | |||
140a878a3d | |||
b62bccf254 | |||
39eb34739a | |||
969b55326c | |||
2c8371654a | |||
5a897cf299 | |||
ef67a0c57f | |||
ef6163e652 | |||
f6985671dd | |||
90a1f7d5c4 | |||
3e114227f8 | |||
6103aa0a46 | |||
b7c6feff28 | |||
b48d907697 | |||
676ddfa065 | |||
c8d00dc191 | |||
0b6865d6c6 | |||
67d05eb65f | |||
152a117d5c | |||
d6626309fd | |||
c3daccd83b | |||
8db62151d2 | |||
bab81a9831 | |||
cc86fee1d1 | |||
386cc5dbb6 | |||
2f7045720a | |||
9889c21aaa | |||
e69cb40de3 | |||
12837e1c17 | |||
9092ac79d4 | |||
0a7d10ba55 | |||
efbd446d18 | |||
c06b0a2371 | |||
0c600cf6e3 | |||
41404057cf | |||
f4e6994634 | |||
b602bd8c83 | |||
30c43521d3 | |||
45f5df371d | |||
43e31c5abb | |||
13c8b13343 | |||
0fc9ec248e | |||
d094a85647 | |||
22c05b0834 | |||
8490921fb3 | |||
ecb9bb96f0 | |||
75463cd8df | |||
c6244d1470 | |||
22ae2d0976 | |||
88b0a239c4 | |||
7c9717bba8 | |||
7f297666ca | |||
d99823e2fd | |||
bb9fb21fac | |||
b64672b23c | |||
930f58718b | |||
2b34c88b69 | |||
45f09ba686 | |||
bb62905bef | |||
7bc9b19418 | |||
e9f7a00910 | |||
a5d6b6db8b | |||
fc496813e2 | |||
2abb414cfb | |||
0e56aaf189 | |||
3412aba46e | |||
347e71af7d | |||
d24df799d3 | |||
01e6b8c7ed | |||
60727c4d2b | |||
03627aa84d | |||
83b8f59297 | |||
c6f454f51d | |||
ccff17599a | |||
fb2773b8f3 | |||
5110121f6e | |||
27cf897239 | |||
5d33a12af4 | |||
08b295603c | |||
3b60503d2b | |||
3c2473bac6 | |||
f9426709ef | |||
e1e44a910e | |||
f371c9066d | |||
85c1927993 | |||
a596b887ff | |||
6cbf99086e | |||
26a3390549 | |||
9a1959f77a | |||
226e662cf1 | |||
7a1a1b80ed | |||
529988bc81 | |||
c17dc1c057 | |||
09a95a692e | |||
247964af62 | |||
5e2bc5c593 | |||
28e657d857 | |||
06ad112998 | |||
cfa1d17afe | |||
3ca6df87b8 | |||
e310bee9e2 | |||
4902244cce | |||
8db97b0b7a | |||
9be04f8d38 | |||
74e2bd7e3e | |||
52d3980d02 | |||
4e9809bcb2 | |||
bd8a4215dd | |||
d23b973e7a | |||
0dbf959548 | |||
20812f446f | |||
27a4d0ce11 | |||
9dec4c7485 | |||
90c87fa6ad | |||
291928feb1 | |||
c9c52fb353 | |||
0bcfcde63d | |||
1c20a62611 | |||
8430927e6b | |||
d2ca7d81c8 | |||
756121acc1 | |||
d7e1175df0 | |||
66cd84e0d5 | |||
506f4ce1e5 | |||
a0387d2835 | |||
9bdd5951d9 | |||
111afcdff1 | |||
85f3dc2fb5 | |||
430f367c2f | |||
d272f96e23 | |||
73a84a7098 | |||
17c8ec8a5d | |||
a1880c3576 | |||
91999e016e | |||
aa966f5de2 | |||
3e593b8221 | |||
440aca86a3 | |||
09f4d6f52d | |||
3f57fa6e0e | |||
ae876d1317 | |||
6de68e2f1f | |||
49527ab495 | |||
0898bca939 | |||
d940387beb | |||
27c45b05cf | |||
90f09d551f | |||
3739588e97 | |||
0730b753f2 | |||
cad693de0f | |||
bf911fc992 | |||
fb6d791ce9 | |||
0f8efce799 | |||
69c1405196 | |||
980491b08f | |||
72e24663ad | |||
363ed5140e | |||
b0f585ab08 | |||
8b67b07580 | |||
5c330ea492 | |||
06eaaf0ac5 | |||
9820271243 | |||
b6507e37ef | |||
c194f6695d | |||
967ae3e1b8 | |||
a5e6eaaebc | |||
d4e9b55fb6 | |||
048bd280dd | |||
8326ab3240 | |||
a7fe983be2 | |||
e1f8e54e34 | |||
2b165944ea | |||
ea11b3f1f8 | |||
3bd87147ab | |||
2f581ffc88 | |||
d61aaac400 | |||
310620fd12 | |||
f9fb72fb0e | |||
095db673c5 | |||
70d18b5b53 | |||
f1796d67f4 | |||
cb38d72ff4 | |||
b72bab49aa | |||
201475e8d8 | |||
c25b9fcf97 | |||
8755a8e188 | |||
127fbfd5a6 | |||
f33dda79e9 | |||
293fa5505b | |||
df1f78e302 | |||
43bb31c6c6 | |||
169869a195 | |||
b691da26af | |||
8e3e45097a | |||
aa43d2f87b | |||
128acbb6eb | |||
5824866a83 | |||
0d58b137a7 | |||
b5c769e1e4 | |||
7f22bd62ab | |||
83f0e7c975 | |||
adef68b4d6 | |||
14f0e9ada8 | |||
ef9e40e82b | |||
41e38e4330 | |||
2c7c3e3c69 | |||
756f5d884f | |||
45fd6f0a41 | |||
75ae4a9159 | |||
37d18d0112 | |||
773fe8f8c5 | |||
4da2b19ea0 | |||
9f3547e35d | |||
5a79358727 | |||
85bfbc13c1 | |||
1a01af9e68 | |||
dd95e901df | |||
9782d8c32e | |||
80764c6f71 | |||
d9e78e4fa8 | |||
10fedd0dfc | |||
315e73c47c | |||
912717ff31 | |||
b857fd1eeb | |||
6dce4f49c2 | |||
15e16148f4 | |||
ae49085481 | |||
11e8aa26f6 | |||
81beb1c788 | |||
6402d61f69 | |||
5a59e44765 | |||
7455b907d1 | |||
579b890446 | |||
19a08f3a43 | |||
a3d7629134 | |||
bc9644e86e | |||
a10c02cb41 | |||
9936e347ff | |||
7d44b8230e | |||
75343eb340 | |||
2548ce86db | |||
5586c29492 | |||
1174473e9c | |||
1d49b3e36b | |||
2830df4190 | |||
143cf89b5f | |||
69c1694900 | |||
01fe7f5fac | |||
9aedef208f | |||
39b6e0efba | |||
f60fa14767 | |||
d900f5c075 | |||
391a9edabb | |||
28a78117eb | |||
eeb594c010 | |||
0bb10d6bb6 | |||
59629a0801 | |||
b5e6319fa9 | |||
c9a3df970b | |||
f72f137261 | |||
ee3c580e88 | |||
05c185a7b1 | |||
b47f80ec76 | |||
ebd438ff5e | |||
331b9c1317 | |||
4a93f58b8b | |||
ebe531bf92 | |||
1779caf5f8 | |||
6ef2121e6a | |||
38cb526f60 | |||
f6a8d04c32 | |||
4b3b5d7c53 | |||
abff302e52 | |||
e1f6679c75 | |||
aaafdf03ce | |||
ee298baa1b | |||
d1abada5b7 | |||
ab36ea097b | |||
8bb66a5eb3 | |||
cfc1e56dd8 | |||
a1c3be21ec | |||
edad8e3f56 | |||
d4a4d81173 | |||
e7470d557d | |||
b634a25ae0 | |||
c1a0af514f | |||
c6afea61f1 | |||
ce10fe92b2 | |||
b81b1fb81c | |||
280b86ec55 | |||
2f5a2ba671 | |||
c45ec6f1be | |||
530470e0ce | |||
ce72ccf9e8 | |||
46d9c87ddc | |||
d736c31fea | |||
a7e9bc97f6 | |||
265703b950 | |||
ae275fa4e4 | |||
3478d5d450 | |||
e72dc16dbe | |||
40a043275d | |||
f161b5cc28 | |||
117d57e121 | |||
3dcce706fd | |||
efb89b83e1 | |||
3d96c2337f | |||
19cfb4eb12 | |||
3d715a2f7b | |||
c7261c295c | |||
1a26f8edd6 | |||
fc887774da | |||
7cbf88a691 | |||
1cb1c139cf | |||
1fd924f7d5 | |||
eb688f2c8e | |||
61cf499b0b | |||
f1f5b45361 | |||
50548fb565 | |||
8f47aa3530 | |||
df7885c9f5 | |||
0f10624b08 | |||
6f1ef33e32 | |||
231773ea76 | |||
e725542703 | |||
2337469753 | |||
55122cd57a | |||
7e0f02f96e | |||
e7ad03cba6 | |||
74be3d3fde | |||
a15ca23469 | |||
de77700da0 | |||
e73fee7156 | |||
72ab35bceb | |||
0f22dce036 | |||
c6036435f0 | |||
d86be245b8 | |||
a26053d3ff | |||
24d5b665e1 | |||
aa98fad338 | |||
9cb6dbbbab | |||
e21718faa9 | |||
b0f7d59e64 | |||
b86829f492 | |||
22929a1671 | |||
86c40f8474 | |||
16b521794c | |||
2a70f4e4c7 | |||
2f31c4c1c5 | |||
1435763383 | |||
05238df89b | |||
772d60d9fe | |||
24086bf0bb | |||
9b0e10e9a7 | |||
995a9e0cf8 | |||
b55f1764b5 | |||
5e9daed2e8 | |||
aa8c5aa2e2 | |||
f2c7946cca | |||
da1fcfd820 | |||
dbeff6f548 | |||
26e60d658a | |||
c2ceb7fba4 | |||
4bfe49cd42 | |||
cee2318110 | |||
cfef76f683 | |||
f56df65d48 | |||
3a40cd79f0 | |||
6acc99729c | |||
99e6500a2d | |||
5c782d6ba8 | |||
4e43d6f769 | |||
3529ee9973 | |||
29aa6a6c1c | |||
7918f3c1fc | |||
2f4e176054 | |||
d4565fdaf3 | |||
2a302aa73a | |||
31b819e9c2 | |||
27daeaff5e | |||
3e9b532409 | |||
c5aa6d17ef | |||
e480b0798e | |||
8a645d5e44 | |||
321193889f | |||
566d3ede04 | |||
8c36aa866a | |||
ed2dbf2db7 | |||
36a0e04604 | |||
8867afdaab | |||
a199772508 | |||
b008f542fa | |||
a78dcfa5f3 | |||
e1bf3d33f8 | |||
ae7d2ab515 | |||
c6adbf602c | |||
1f7a41c963 | |||
f4f614f3a9 | |||
94139c351f | |||
fc5b128b43 | |||
68a799af2e | |||
16d9c60a0e | |||
c0b5e7a672 | |||
6932b29acb | |||
c2a38c05aa | |||
8bea667a0b | |||
800c8f196f | |||
42231f5719 | |||
db87baeb98 | |||
c4c43f5a77 | |||
0ae77753f3 | |||
5f0baaac73 | |||
b5b6ece65a | |||
4282297c24 | |||
9c96b8affc | |||
132829e5e2 | |||
4a414420e9 | |||
fb6335ab60 | |||
89bd008445 | |||
caafb41eb5 | |||
31b81a7439 | |||
f7b1973358 | |||
32f01da49a | |||
59684c97b0 | |||
a32a0a3a97 | |||
96f5b0929d | |||
8e6cf7fca8 | |||
fdbe8741c9 | |||
775fb2c340 | |||
b60f594798 | |||
cc49790bdb | |||
a4bc19c530 | |||
f7985dbdb7 | |||
0bdcb5c1e0 | |||
a0d25db4a5 | |||
625474c4e2 | |||
1cd2a6328a | |||
d6bafe4fe3 | |||
6a2ef15355 | |||
a8ca560503 | |||
ad361808ec | |||
c47639f2b1 | |||
ba90a85f7b | |||
d3b569557f | |||
c9090ffa31 | |||
341bf39d23 | |||
3778ac26aa | |||
6cfc6f5bb2 | |||
47a3b4d56b | |||
c72ed991ad | |||
78bfdf78ea | |||
a24ee6add4 | |||
df3074fdfe | |||
f7424d5aeb | |||
a593ffa6f3 | |||
761c6d0df7 | |||
3e65d1458e | |||
a4b5cb8376 | |||
c785a1e474 | |||
3adc472f06 | |||
e7081b8b7c | |||
9b3a548f6f | |||
90b3502bb8 | |||
e19b31db29 | |||
bd015f14e8 | |||
ca7ba12fc6 | |||
ae05ec69c4 | |||
92dc3b91d8 | |||
8bd697b316 | |||
eda4c3eb4c | |||
4d0c2ed1f6 | |||
eda6a5d52a | |||
c1c54ed0f2 | |||
6b73d09ba1 | |||
ac85cbb28a | |||
b0cebdba6b | |||
933a9112da | |||
8c37b7e8f2 | |||
c8e909f8c9 | |||
69ae3634c7 | |||
95248f46a1 | |||
b3c7df1783 | |||
c56679e8e1 | |||
041c599511 | |||
6343f71be5 | |||
89f32f808f | |||
7ae23adaff | |||
a08f50badd | |||
0b6e75a85e | |||
4291758079 | |||
b44d36cf95 | |||
a038bb9ae3 | |||
9d28147acb | |||
d116861c8e | |||
9a70f1a1d9 | |||
8516473340 | |||
cab69f689f | |||
822e83ebb0 | |||
b2bc50dbd1 | |||
e748adda2e | |||
630d93150a | |||
76f1f9f1e3 | |||
c47d85b038 | |||
197cf09689 | |||
e67ebb7f70 | |||
3524946581 | |||
9276dad42c | |||
2a2f9a9a19 | |||
909e70bd61 | |||
8ac9719832 | |||
3eff7c6f59 | |||
17fbbfba91 | |||
b232dded77 | |||
f50affaf9c | |||
463754bf16 | |||
a45b27e7f9 | |||
af3ea16acb | |||
fc75220d63 | |||
00822c3415 | |||
3dca9d522a | |||
de996ec50b |
11
.bazelrc
Normal file
11
.bazelrc
Normal file
@ -0,0 +1,11 @@
|
||||
# This prevents the compiler codegen step from requiring certain packages from
|
||||
# being built twice -- one for host and one for target.
|
||||
#
|
||||
# In any case, since we don't have architecture dependent outputs, setting this
|
||||
# to false will not be a problem.
|
||||
build --distinct_host_configuration=false
|
||||
|
||||
build --verbose_failures --show_task_finish
|
||||
test --test_output=errors --keep_going
|
||||
|
||||
build --strategy=TypeScriptCompile=worker --worker_max_instances=2
|
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -3,3 +3,6 @@
|
||||
|
||||
# JS files must always use LF for tools to work
|
||||
*.js eol=lf
|
||||
|
||||
# Must keep Windows line ending to be parsed correctly
|
||||
scripts/windows/packages.txt eol=crlf
|
||||
|
33
.github/ISSUE_TEMPLATE.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
```
|
||||
[ ] bug report => search github for a similar issue or PR before submitting
|
||||
[ ] feature request
|
||||
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
```
|
||||
|
||||
**Current behavior**
|
||||
|
||||
|
||||
**Expected/desired behavior**
|
||||
|
||||
|
||||
**Reproduction of the problem**
|
||||
If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
|
||||
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
|
||||
|
||||
|
||||
**Please tell us about your environment:**
|
||||
|
||||
* **Angular version:** 2.0.0-rc.X
|
||||
|
||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
**Please check if the PR fulfills these requirements**
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit-message-format
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
|
||||
**What kind of change does this PR introduce?** (check one with "x")
|
||||
```
|
||||
[ ] Bugfix
|
||||
[ ] Feature
|
||||
[ ] Code style update (formatting, local variables)
|
||||
[ ] Refactoring (no functional changes, no api changes)
|
||||
[ ] Build related changes
|
||||
[ ] CI related changes
|
||||
[ ] Other... Please describe:
|
||||
```
|
||||
|
||||
**What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
|
||||
|
||||
**What is the new behavior?**
|
||||
|
||||
|
||||
|
||||
**Does this PR introduce a breaking change?** (check one with "x")
|
||||
```
|
||||
[ ] Yes
|
||||
[ ] No
|
||||
```
|
||||
|
||||
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
|
||||
|
||||
|
||||
**Other information**:
|
||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -21,10 +21,6 @@ tmp
|
||||
*.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
|
||||
@ -48,3 +44,10 @@ npm-debug.log
|
||||
|
||||
# built dart payload tests
|
||||
/modules_dart/payload/**/build
|
||||
|
||||
# rollup-test output
|
||||
/modules/rollup-test/dist/
|
||||
|
||||
# bazel runtime folders
|
||||
/bazel-*
|
||||
!bazel-run.sh
|
||||
|
284
.travis.yml
284
.travis.yml
@ -1,132 +1,204 @@
|
||||
language: node_js
|
||||
# We have to use the Java image to get access to java 8, which is required for
|
||||
# bazel.
|
||||
language: java
|
||||
sudo: false
|
||||
node_js:
|
||||
- '4.2.1'
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
addons:
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
# needed to install g++ that is used by npms's native modules
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
# The following are needed for bazel.
|
||||
- pkg-config
|
||||
- zip
|
||||
- unzip
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3sync
|
||||
- g3_v2_0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- $HOME/.pub-cache
|
||||
- ./node_modules
|
||||
- ./.chrome/chromium
|
||||
- $HOME/bazel-cache
|
||||
# - $HOME/.pub-cache
|
||||
|
||||
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
|
||||
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
|
||||
- BROWSER_STACK_USERNAME=angularteam1
|
||||
- BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
- ARCH=linux-x64
|
||||
- DART_DEV_VERSION=latest
|
||||
- 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="
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - ARCH=linux-x64
|
||||
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
matrix:
|
||||
# Order: a slower build first, so that we don't occupy an idle travis worker 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=browserstack 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=build_only DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=payload DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- CI_MODE=js
|
||||
- CI_MODE=e2e
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
# - CI_MODE=saucelabs_optional
|
||||
# - CI_MODE=browserstack_optional
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: "MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=browserstack DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
# TODO(alxhub): remove when dartdoc #1039 is in dev channel
|
||||
- env: "MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "CI_MODE=saucelabs_optional"
|
||||
- env: "CI_MODE=browserstack_optional"
|
||||
|
||||
addons:
|
||||
firefox: "38.0"
|
||||
|
||||
before_install:
|
||||
- node tools/analytics/build-analytics start ci job
|
||||
- node tools/analytics/build-analytics start ci 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} ${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'
|
||||
- node tools/analytics/build-analytics success ci before_install
|
||||
|
||||
install:
|
||||
- node tools/analytics/build-analytics start ci install
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules || true
|
||||
# Install npm dependecies
|
||||
- npm install
|
||||
- node tools/analytics/build-analytics success ci install
|
||||
- ./scripts/ci-lite/install.sh
|
||||
|
||||
before_script:
|
||||
- node tools/analytics/build-analytics start ci before_script
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./scripts/ci/presubmit-queue-setup.sh
|
||||
- node tools/analytics/build-analytics success ci before_script
|
||||
|
||||
|
||||
script:
|
||||
- node tools/analytics/build-analytics start ci script
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
- node tools/analytics/build-analytics success ci script
|
||||
- ./scripts/ci-lite/test.sh
|
||||
|
||||
after_script:
|
||||
- node tools/analytics/build-analytics start ci after_script
|
||||
- ./scripts/ci/print-logs.sh
|
||||
- ./scripts/ci/after-script.sh
|
||||
- ./scripts/publish/publish-build-artifacts.sh
|
||||
- node tools/analytics/build-analytics success ci after_script
|
||||
- if [[ $TRAVIS_TEST_RESULT -eq 0 ]]; then node tools/analytics/build-analytics success ci job; else node tools/analytics/build-analytics error ci job; fi
|
||||
- ./scripts/ci-lite/cleanup.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# 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/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
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = build_only"
|
||||
#branches:
|
||||
# except:
|
||||
# - g3_v2_0
|
||||
#
|
||||
#cache:
|
||||
# directories:
|
||||
# - $HOME/.pub-cache
|
||||
# - $HOME/.chrome/chromium
|
||||
#
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
#
|
||||
#env:
|
||||
# global:
|
||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
# - CXX=g++-4.8
|
||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - SAUCE_USERNAME=angular-ci
|
||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
# - BROWSER_STACK_USERNAME=angularteam1
|
||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
# - ARCH=linux-x64
|
||||
# - DART_DEV_VERSION=latest
|
||||
# - DART_STABLE_VERSION=latest
|
||||
# - DART_CHANNEL=stable
|
||||
# - DART_VERSION=$DART_STABLE_VERSION
|
||||
# # 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:
|
||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
# - MODE=dart
|
||||
# - MODE=dart DART_CHANNEL=dev
|
||||
# - MODE=saucelabs_required
|
||||
# - MODE=browserstack_required
|
||||
# - MODE=saucelabs_optional
|
||||
# - MODE=browserstack_optional
|
||||
# - MODE=dart_ddc
|
||||
# - MODE=js
|
||||
# - MODE=router
|
||||
# - MODE=build_only
|
||||
# - MODE=typescript_next
|
||||
# - MODE=lint
|
||||
#
|
||||
#matrix:
|
||||
# allow_failures:
|
||||
# - env: "MODE=saucelabs_optional"
|
||||
# - env: "MODE=browserstack_optional"
|
||||
#
|
||||
#addons:
|
||||
# firefox: "38.0"
|
||||
# apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
# packages:
|
||||
# - g++-4.8
|
||||
#
|
||||
#before_install:
|
||||
# - node tools/analytics/build-analytics start ci job
|
||||
# - node tools/analytics/build-analytics start ci before_install
|
||||
# - echo ${TSDRC} > .tsdrc
|
||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
||||
# - export DISPLAY=:99.0
|
||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
||||
# - ./scripts/ci/init_android.sh
|
||||
# - sh -e /etc/init.d/xvfb start
|
||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
# - node tools/analytics/build-analytics success ci before_install
|
||||
#
|
||||
#install:
|
||||
# - node tools/analytics/build-analytics start ci install
|
||||
# # Install version of npm that we are locked against
|
||||
# - npm install -g npm@3.5.3
|
||||
# # Install version of Chromium that we are locked against
|
||||
# - ./scripts/ci/install_chromium.sh
|
||||
# # Install version of Dart based on the matrix build variables
|
||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
# # Print the size of caches to ease debugging
|
||||
# - du -sh ./node_modules || true
|
||||
# # Install npm dependecies
|
||||
# # check-node-modules will exit(1) if we don't need to install
|
||||
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
||||
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
||||
# - node tools/analytics/build-analytics success ci install
|
||||
#
|
||||
#before_script:
|
||||
# - node tools/analytics/build-analytics start ci before_script
|
||||
# - mkdir -p $LOGS_DIR
|
||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
||||
# - node tools/analytics/build-analytics success ci before_script
|
||||
#
|
||||
#script:
|
||||
# - node tools/analytics/build-analytics start ci script
|
||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
||||
# - node tools/analytics/build-analytics success ci script
|
||||
#
|
||||
#after_script:
|
||||
# - node tools/analytics/build-analytics start ci after_script
|
||||
# - ./scripts/ci/print-logs.sh
|
||||
# - ./scripts/ci/after-script.sh
|
||||
# - ./scripts/publish/publish-build-artifacts.sh
|
||||
# - node tools/analytics/build-analytics success ci after_script
|
||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
||||
#
|
||||
#notifications:
|
||||
# webhooks:
|
||||
# urls:
|
||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
||||
# - https://buildtimetrend.herokuapp.com/travis
|
||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
||||
# on_success: always # options: [always|never|change] default: always
|
||||
# on_failure: always # options: [always|never|change] default: always
|
||||
# on_start: never # default: never
|
||||
# slack:
|
||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
920
BUILD
Normal file
920
BUILD
Normal file
@ -0,0 +1,920 @@
|
||||
package(default_visibility=["//visibility:public"])
|
||||
|
||||
load("//build_defs:nodejs.bzl", "nodejs_binary", "nodejs_test")
|
||||
load("//build_defs:typescript.bzl", "ts_library", "ts_ext_library")
|
||||
load("//build_defs:jasmine.bzl", "jasmine_node_test")
|
||||
load("//build_defs:karma.bzl", "karma_test")
|
||||
load("//build_defs:bundle.bzl", "js_bundle")
|
||||
load("//build_defs:protractor.bzl", "protractor_test")
|
||||
load("//build_defs:ts_api_guardian.bzl", "public_api", "public_api_test")
|
||||
load("//build_defs:npm_package.bzl", "ts_npm_package")
|
||||
|
||||
# This imports node_modules targets from a generated file.
|
||||
load("//build_defs:node_modules_index.bzl", "node_modules_index")
|
||||
node_modules_index(glob)
|
||||
|
||||
###############################################################################
|
||||
# Tools
|
||||
###############################################################################
|
||||
nodejs_binary(
|
||||
name = "tsc-wrapped_bootstrap",
|
||||
srcs = [
|
||||
'tools/@angular/tsc-wrapped/bootstrap.js',
|
||||
'//:typescript',
|
||||
'//:minimist',
|
||||
],
|
||||
entry_point = 'tools/@angular/tsc-wrapped/bootstrap.js',
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "tsc-wrapped",
|
||||
srcs = glob(
|
||||
[
|
||||
"tools/@angular/tsc-wrapped/index.ts",
|
||||
"tools/@angular/tsc-wrapped/src/**/*.ts",
|
||||
],
|
||||
),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:typescript",
|
||||
"//:tsickle",
|
||||
],
|
||||
data = [
|
||||
"tools/@angular/tsc-wrapped/worker_protocol.proto",
|
||||
"//:minimist",
|
||||
"//:bytebuffer",
|
||||
"//:protobufjs",
|
||||
],
|
||||
tsconfig = "tools/@angular/tsc-wrapped/tsconfig.json",
|
||||
compiler = "//:tsc-wrapped_bootstrap",
|
||||
module_name = "@angular/tsc-wrapped",
|
||||
root_dir = "tools/@angular/tsc-wrapped",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "tsc-wrapped_test_module",
|
||||
srcs = glob(["tools/@angular/tsc-wrapped/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_jasmine",
|
||||
"//:typescript",
|
||||
"//:tsc-wrapped",
|
||||
],
|
||||
tsconfig = "tools/@angular/tsc-wrapped/tsconfig.json",
|
||||
root_dir = "tools/@angular/tsc-wrapped/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "tsc-wrapped_test",
|
||||
srcs = [":tsc-wrapped_test_module"],
|
||||
size = "small",
|
||||
args = ["--node_path=modules:tools"],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "tsc-wrapped_bin",
|
||||
srcs = [":tsc-wrapped"],
|
||||
entry_point = "tools/@angular/tsc-wrapped/src/worker.js",
|
||||
)
|
||||
|
||||
test_suite(
|
||||
name = "tool_tests",
|
||||
tests = [
|
||||
"//:tsc-wrapped_test",
|
||||
"//tools/ibazel:ibazel_test",
|
||||
"//build_defs/tests/typescript:assert_test",
|
||||
"//build_defs/tests/nodejs:all_tests",
|
||||
],
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Packages
|
||||
###############################################################################
|
||||
ts_library(
|
||||
name = "jasmine_helper",
|
||||
srcs = [
|
||||
"modules/jasmine_helper.ts"
|
||||
],
|
||||
deps = [
|
||||
"//:core",
|
||||
"//:platform-server",
|
||||
],
|
||||
data = [
|
||||
"//:source-map-support",
|
||||
"//:reflect-metadata",
|
||||
"//:zone.js",
|
||||
"//:parse5",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
)
|
||||
|
||||
ts_ext_library(
|
||||
name = "es6-subset",
|
||||
declarations = ["modules/es6-subset.d.ts"],
|
||||
ambient = True,
|
||||
entry_point = "modules/es6-subset.d.ts",
|
||||
)
|
||||
|
||||
ts_ext_library(
|
||||
name = "dummy_system",
|
||||
declarations = ["modules/system.d.ts"],
|
||||
ambient = True,
|
||||
entry_point = "modules/system.d.ts",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "facade",
|
||||
srcs = glob(["modules/@angular/facade/src/**/*.ts"]),
|
||||
deps = [
|
||||
"//:zone.js",
|
||||
"//:rxjs",
|
||||
"//:es6-subset",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
module_name = "@angular/facade",
|
||||
root_dir = "modules/@angular/facade",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "common",
|
||||
srcs = glob(
|
||||
["modules/@angular/common/**/*.ts"],
|
||||
exclude = ["modules/@angular/common/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:es6-subset",
|
||||
],
|
||||
tsconfig = "modules/@angular/common/tsconfig-es5.json",
|
||||
module_name = "@angular/common",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "common_test_module",
|
||||
srcs = glob(["modules/@angular/common/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:platform-server",
|
||||
"//:facade",
|
||||
"//:es6-subset",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:common",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/common/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "compiler-cli",
|
||||
srcs = glob(
|
||||
["modules/@angular/compiler-cli/**/*.ts"],
|
||||
exclude = [
|
||||
"modules/@angular/compiler-cli/test/**/*.ts",
|
||||
"modules/@angular/compiler-cli/integrationtest/**/*.ts",
|
||||
]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-server",
|
||||
"//:platform-browser",
|
||||
"//:tsc-wrapped",
|
||||
],
|
||||
tsconfig = "modules/@angular/compiler-cli/tsconfig-es5.json",
|
||||
module_name = "@angular/compiler-cli",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "compiler-cli_test_module",
|
||||
srcs = glob(["modules/@angular/compiler-cli/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:compiler-cli",
|
||||
"//:tsc-wrapped",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:compiler-cli",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/compiler-cli/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "compiler",
|
||||
srcs = glob(
|
||||
["modules/@angular/compiler/**/*.ts"],
|
||||
exclude = ["modules/@angular/compiler/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
],
|
||||
tsconfig = "modules/@angular/compiler/tsconfig-es5.json",
|
||||
module_name = "@angular/compiler",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "compiler_test_module",
|
||||
srcs = glob(["modules/@angular/compiler/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:compiler",
|
||||
"//:facade",
|
||||
"//:es6-subset",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:compiler",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/compiler/test",
|
||||
# Required for compiling codegen.
|
||||
module_name = "@angular/compiler/test",
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "compiler_test_codegen_bin",
|
||||
srcs = [":compiler_test_module"],
|
||||
deps = [
|
||||
"reflect-metadata",
|
||||
],
|
||||
entry_point = "modules/@angular/compiler/test/output/output_emitter_codegen.js",
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "compiler_test_codegen_ts",
|
||||
outs = [
|
||||
"modules/@angular/compiler/test/output/output_emitter_generated_typed.ts",
|
||||
"modules/@angular/compiler/test/output/output_emitter_generated_untyped.ts",
|
||||
],
|
||||
tools = [
|
||||
# This has to be put in tools so that its runfiles tree is also built.
|
||||
":compiler_test_codegen_bin",
|
||||
],
|
||||
cmd = "$(location :compiler_test_codegen_bin) --node_path=modules/ $(OUTS)",
|
||||
output_to_bindir = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "compiler_test_codegen_js",
|
||||
srcs = [":compiler_test_codegen_ts"],
|
||||
deps = [
|
||||
":core",
|
||||
":compiler",
|
||||
":compiler_test_module",
|
||||
],
|
||||
tsconfig = "tools/cjs-jasmine/tsconfig-output_emitter_codegen.json",
|
||||
root_dir = "modules/@angular/compiler/test",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "core",
|
||||
srcs = glob(
|
||||
["modules/@angular/core/**/*.ts"],
|
||||
exclude = ["modules/@angular/core/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:rxjs",
|
||||
"//:dummy_system",
|
||||
],
|
||||
tsconfig = "modules/@angular/core/tsconfig-es5.json",
|
||||
module_name = "@angular/core",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "core_test_module",
|
||||
srcs = glob(["modules/@angular/core/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:core",
|
||||
"//:facade",
|
||||
"//:es6-subset",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:core",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/core/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "forms",
|
||||
srcs = glob(
|
||||
["modules/@angular/forms/**/*.ts"],
|
||||
exclude = ["modules/@angular/forms/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
],
|
||||
tsconfig = "modules/@angular/forms/tsconfig-es5.json",
|
||||
module_name = "@angular/forms",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "forms_test_module",
|
||||
srcs = glob(
|
||||
["modules/@angular/forms/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:forms",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:forms",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/forms/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "http",
|
||||
srcs = glob(
|
||||
["modules/@angular/http/**/*.ts"],
|
||||
exclude = ["modules/@angular/http/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:platform-browser",
|
||||
],
|
||||
tsconfig = "modules/@angular/http/tsconfig-es5.json",
|
||||
module_name = "@angular/http",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "platform-browser",
|
||||
srcs = glob(
|
||||
["modules/@angular/platform-browser/**/*.ts"],
|
||||
exclude = ["modules/@angular/platform-browser/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_hammerjs",
|
||||
"//:_types_jasmine",
|
||||
"//:_types_protractor",
|
||||
"//:zone.js",
|
||||
"//:_types_selenium-webdriver",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:facade",
|
||||
],
|
||||
tsconfig = "modules/@angular/platform-browser/tsconfig-es5.json",
|
||||
module_name = "@angular/platform-browser",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "platform-browser_test_module",
|
||||
srcs = glob(["modules/@angular/platform-browser/test/**/*.ts"]),
|
||||
data = glob(
|
||||
[
|
||||
"modules/@angular/platform-browser/test/static_assets/**",
|
||||
"modules/@angular/platform-browser/test/browser/static_assets/**",
|
||||
]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:core",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:core",
|
||||
"//:platform-browser",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/platform-browser/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "http_test_module",
|
||||
srcs = glob(["modules/@angular/http/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:http",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:http",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/http/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "platform-browser-dynamic",
|
||||
srcs = glob(
|
||||
["modules/@angular/platform-browser-dynamic/**/*.ts"],
|
||||
exclude = ["modules/@angular/platform-browser-dynamic/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
],
|
||||
tsconfig = "modules/@angular/platform-browser-dynamic/tsconfig-es5.json",
|
||||
module_name = "@angular/platform-browser-dynamic",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "platform-browser-dynamic_test_module",
|
||||
srcs = glob(["modules/@angular/platform-browser-dynamic/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:platform-browser-dynamic",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/platform-browser-dynamic/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "platform-server",
|
||||
srcs = glob(
|
||||
["modules/@angular/platform-server/**/*.ts"],
|
||||
exclude = [
|
||||
"modules/@angular/platform-server/platform_browser_dynamic_testing_private.ts",
|
||||
"modules/@angular/platform-server/test/**/*.ts",
|
||||
]),
|
||||
deps = [
|
||||
"//:_types_jasmine",
|
||||
"//:_types_node",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
],
|
||||
tsconfig = "modules/@angular/platform-server/tsconfig-es5.json",
|
||||
module_name = "@angular/platform-server",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "platform-server_test_module",
|
||||
srcs = glob(["modules/@angular/platform-server/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:platform-server",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:platform-server",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/platform-server/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "router",
|
||||
srcs = glob(
|
||||
["modules/@angular/router/**/*.ts"],
|
||||
exclude = ["modules/@angular/router/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
],
|
||||
tsconfig = "modules/@angular/router/tsconfig-es5.json",
|
||||
module_name = "@angular/router",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "router_test_module",
|
||||
srcs = glob(["modules/@angular/router/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:router",
|
||||
"//:platform-browser",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:router",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/router/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "upgrade",
|
||||
srcs = glob(
|
||||
["modules/@angular/upgrade/**/*.ts"],
|
||||
exclude = ["modules/@angular/upgrade/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:compiler",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
],
|
||||
tsconfig = "modules/@angular/upgrade/tsconfig-es5.json",
|
||||
module_name = "@angular/upgrade",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "upgrade_test_module",
|
||||
srcs = glob(["modules/@angular/upgrade/test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_jasmine",
|
||||
"//:zone.js",
|
||||
"//:core",
|
||||
"//:platform-browser",
|
||||
"//:upgrade",
|
||||
"//:facade",
|
||||
],
|
||||
deps_use_internal = [
|
||||
"//:upgrade",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/@angular/upgrade/test",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "compiler_test",
|
||||
srcs = [":compiler_test_module", ":compiler_test_codegen_js"],
|
||||
helpers = [":jasmine_helper"],
|
||||
size = "small",
|
||||
args = ["--node_path=modules:tools"],
|
||||
)
|
||||
|
||||
JASMINE_TESTABLE = [
|
||||
"core",
|
||||
"common",
|
||||
"compiler",
|
||||
"compiler-cli",
|
||||
"http",
|
||||
"platform-server",
|
||||
"router",
|
||||
]
|
||||
|
||||
[
|
||||
jasmine_node_test(
|
||||
name = pkg + "_test",
|
||||
srcs = [":{}_test_module".format(pkg)],
|
||||
helpers = [":jasmine_helper"],
|
||||
size = "small",
|
||||
args = ["--node_path=modules:tools"],
|
||||
flaky = pkg == "platform-server",
|
||||
)
|
||||
for pkg in JASMINE_TESTABLE
|
||||
if pkg != "compiler"
|
||||
]
|
||||
|
||||
test_suite(
|
||||
name = "jasmine_tests",
|
||||
tests = [":{}_test".format(p) for p in JASMINE_TESTABLE],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "empty_module",
|
||||
srcs = ["modules/empty.ts"],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
)
|
||||
|
||||
KARMA_DATA = [
|
||||
":es6-shim",
|
||||
":karma-browserstack-launcher",
|
||||
":karma-chrome-launcher",
|
||||
":karma-jasmine",
|
||||
":karma-sauce-launcher",
|
||||
":karma-sourcemap-loader",
|
||||
":reflect-metadata",
|
||||
":source-map",
|
||||
":systemjs",
|
||||
]
|
||||
|
||||
karma_test(
|
||||
name = "karma_test",
|
||||
srcs = [
|
||||
":core_test_module",
|
||||
":common_test_module",
|
||||
":compiler_test_module",
|
||||
":compiler_test_codegen_js",
|
||||
":forms_test_module",
|
||||
":http_test_module",
|
||||
":platform-browser_test_module",
|
||||
":platform-browser-dynamic_test_module",
|
||||
":platform-server_test_module",
|
||||
":upgrade_test_module",
|
||||
":empty_module",
|
||||
"shims_for_IE.js",
|
||||
"test-main.js",
|
||||
],
|
||||
data = KARMA_DATA + [
|
||||
":angular",
|
||||
"browser-providers.conf.js",
|
||||
"tools/karma/reporter.js",
|
||||
"tools/karma/ibazel_watcher.js",
|
||||
],
|
||||
config = "karma-js.conf.js",
|
||||
local = True,
|
||||
)
|
||||
|
||||
karma_test(
|
||||
name = "router_karma_test",
|
||||
srcs = [
|
||||
":router_test_module",
|
||||
"modules/@angular/router/karma-test-shim.js",
|
||||
],
|
||||
data = KARMA_DATA + [
|
||||
"browser-providers.conf.js",
|
||||
"tools/karma/ibazel_watcher.js",
|
||||
],
|
||||
config = "modules/@angular/router/karma.conf.js",
|
||||
size = "small",
|
||||
local = True,
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Packaging and end to end tests
|
||||
###############################################################################
|
||||
ESM_PACKAGES = [
|
||||
"core",
|
||||
"common",
|
||||
"compiler",
|
||||
"forms",
|
||||
"http",
|
||||
"platform-browser",
|
||||
"platform-browser-dynamic",
|
||||
"platform-server",
|
||||
"router",
|
||||
"upgrade",
|
||||
]
|
||||
|
||||
NON_ESM_PACKAGES = [
|
||||
"compiler-cli",
|
||||
]
|
||||
|
||||
ALL_PACKAGES = ESM_PACKAGES + NON_ESM_PACKAGES + ["tsc-wrapped"]
|
||||
|
||||
[
|
||||
js_bundle(
|
||||
name = pkg + "_bundle",
|
||||
srcs = [":" + pkg],
|
||||
output = "modules/@angular/{}/dist/index.js".format(pkg, pkg),
|
||||
entry_point = "modules/@angular/{}/esm/index.js".format(pkg),
|
||||
rollup_config = "modules/@angular/{}/rollup.config.js".format(pkg),
|
||||
banner = "modules/@angular/license-banner.txt",
|
||||
)
|
||||
for pkg in ESM_PACKAGES
|
||||
]
|
||||
|
||||
ts_npm_package(
|
||||
name = "tsc-wrapped_package",
|
||||
srcs = [":tsc-wrapped"],
|
||||
manifest = "tools/@angular/tsc-wrapped/package.json",
|
||||
module_name = "@angular/tsc-wrapped",
|
||||
strip_prefix = "/tools/@angular/tsc-wrapped/dist",
|
||||
esm = False,
|
||||
)
|
||||
|
||||
[
|
||||
ts_npm_package(
|
||||
name = pkg + "_package",
|
||||
srcs = [":{}".format(pkg)],
|
||||
data = [":{}_bundle".format(pkg)] if pkg in ESM_PACKAGES else [],
|
||||
manifest = "modules/@angular/{}/package.json".format(pkg),
|
||||
module_name = "@angular/" + pkg,
|
||||
# Prefix / avoids bug https://github.com/bazelbuild/bazel/issues/1604
|
||||
strip_prefix = "/modules/@angular/{}/dist".format(pkg),
|
||||
esm = pkg in ESM_PACKAGES,
|
||||
)
|
||||
for pkg in ESM_PACKAGES + NON_ESM_PACKAGES
|
||||
]
|
||||
|
||||
filegroup(
|
||||
name = "all_packages",
|
||||
srcs = [":{}_package".format(p) for p in ALL_PACKAGES],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "playground",
|
||||
srcs = glob(["modules/playground/src/**/*.ts"]),
|
||||
deps = [
|
||||
"//:core",
|
||||
"//:common",
|
||||
"//:forms",
|
||||
"//:http",
|
||||
"//:platform-browser",
|
||||
"//:platform-browser-dynamic",
|
||||
"//:router",
|
||||
"//:upgrade",
|
||||
"//:facade",
|
||||
],
|
||||
data = glob(
|
||||
["modules/playground/src/**/*"],
|
||||
exclude = ["modules/playground/src/**/*.ts"],
|
||||
),
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "e2e_util",
|
||||
srcs = glob(["modules/e2e_util/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_node",
|
||||
"//:_types_protractor",
|
||||
"//:_types_selenium-webdriver",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
root_dir = "modules/e2e_util",
|
||||
module_name = "e2e_util",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "playground_test_module",
|
||||
srcs = glob(["modules/playground/e2e_test/**/*.ts"]),
|
||||
deps = [
|
||||
"//:_types_jasmine",
|
||||
"//:e2e_util",
|
||||
],
|
||||
tsconfig = "modules/tsconfig.json",
|
||||
is_leaf = True,
|
||||
)
|
||||
|
||||
protractor_test(
|
||||
name = "playground_test",
|
||||
srcs = [":playground_test_module"],
|
||||
data = [":{}_bundle".format(p) for p in ESM_PACKAGES] + [
|
||||
":core", # Needed for @angular/core/src/facade
|
||||
":facade",
|
||||
":playground",
|
||||
":core-js",
|
||||
":zone.js",
|
||||
":systemjs",
|
||||
":base64-js",
|
||||
":reflect-metadata",
|
||||
":rxjs",
|
||||
":angular",
|
||||
"favicon.ico",
|
||||
],
|
||||
config = "protractor-bazel.conf.js",
|
||||
local = True,
|
||||
args = ["--node_path=modules:tools"],
|
||||
)
|
||||
|
||||
public_api(
|
||||
name = "public_api",
|
||||
srcs = [
|
||||
":core_compat",
|
||||
":common_compat",
|
||||
":platform-browser_compat",
|
||||
":platform-browser-dynamic_compat",
|
||||
":platform-server_compat",
|
||||
":http_compat",
|
||||
":forms_compat",
|
||||
":router_compat",
|
||||
":upgrade_compat",
|
||||
],
|
||||
entry_points = [
|
||||
"modules/@angular/core/compat/index.d.ts",
|
||||
"modules/@angular/core/compat/testing.d.ts",
|
||||
"modules/@angular/common/compat/index.d.ts",
|
||||
"modules/@angular/common/compat/testing.d.ts",
|
||||
"modules/@angular/upgrade/compat/index.d.ts",
|
||||
"modules/@angular/platform-browser/compat/index.d.ts",
|
||||
"modules/@angular/platform-browser/compat/testing.d.ts",
|
||||
"modules/@angular/platform-browser-dynamic/compat/index.d.ts",
|
||||
"modules/@angular/platform-browser-dynamic/compat/testing.d.ts",
|
||||
"modules/@angular/platform-server/compat/index.d.ts",
|
||||
"modules/@angular/platform-server/compat/testing.d.ts",
|
||||
"modules/@angular/http/compat/index.d.ts",
|
||||
"modules/@angular/http/compat/testing.d.ts",
|
||||
"modules/@angular/forms/compat/index.d.ts",
|
||||
"modules/@angular/router/compat/index.d.ts",
|
||||
],
|
||||
root_dir = "modules/@angular",
|
||||
out_dir = "tools/public_api_guard",
|
||||
arguments = [
|
||||
"--stripExportPattern ^__",
|
||||
"--allowModuleIdentifiers jasmine",
|
||||
"--allowModuleIdentifiers protractor",
|
||||
"--allowModuleIdentifiers angular",
|
||||
"--onStabilityMissing error",
|
||||
],
|
||||
)
|
||||
|
||||
public_api_test(
|
||||
name = "public_api_test",
|
||||
srcs = glob(["tools/public_api_guard/**/*"]),
|
||||
public_api = ":public_api",
|
||||
size = "small",
|
||||
)
|
||||
|
||||
nodejs_test(
|
||||
name = "check_cycle_test",
|
||||
srcs = [
|
||||
"//build_defs:check_cycle.js",
|
||||
],
|
||||
deps = [
|
||||
":madge",
|
||||
],
|
||||
entry_point = "check_cycle.js",
|
||||
data = [
|
||||
":core",
|
||||
":common",
|
||||
":compiler",
|
||||
":compiler-cli",
|
||||
":forms",
|
||||
":http",
|
||||
":platform-browser",
|
||||
":platform-browser-dynamic",
|
||||
":platform-server",
|
||||
":router",
|
||||
":upgrade",
|
||||
":tsc-wrapped",
|
||||
],
|
||||
size = "small",
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "offline_compiler_test",
|
||||
srcs = ["scripts/ci-lite/offline_compiler_test.sh"],
|
||||
data = [
|
||||
"//:all_packages",
|
||||
"package.json",
|
||||
] + glob(["modules/@angular/compiler-cli/integrationtest/**"]),
|
||||
# This currently uses external npm, so we need an unsandboxed environment.
|
||||
# If your npm is not in the standard PATH, you will also need to pass the
|
||||
# flag --test_env=PATH to "bazel test".
|
||||
local = True,
|
||||
)
|
1812
CHANGELOG.md
1812
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -11,8 +11,11 @@ Someone with committer access will do the rest.
|
||||
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`.
|
||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
||||
so the caretaker reviews the changes manually.
|
||||
|
||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
||||
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}`.
|
||||
@ -26,6 +29,6 @@ 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.
|
||||
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` 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)
|
||||
|
@ -95,7 +95,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular 2 test suites for JS and Dart to ensure tests are still passing.
|
||||
* Re-run the Angular 2 test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
@ -166,6 +166,19 @@ 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.
|
||||
|
||||
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
|
||||
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||||
|
||||
```
|
||||
docs(changelog): update change log to beta.5
|
||||
```
|
||||
```
|
||||
fix(release): need to depend on latest rxjs and zone.js
|
||||
|
||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
@ -180,7 +193,8 @@ Must be one of the following:
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
* **build** Changes that affect the build system, CI configuration or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
* **chore**: Other changes that don't modify `src` or `test` files
|
||||
|
||||
### Scope
|
||||
@ -224,8 +238,8 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
[github]: https://github.com/angular/angular
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[runnable]: http://runnable.com/
|
||||
[runnable]: http://runnable.com
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
||||
|
347
DEVELOPER.md
347
DEVELOPER.md
@ -1,19 +1,13 @@
|
||||
# Building and Testing Angular 2 for JS and Dart
|
||||
# Building and Testing Angular 2 for JS
|
||||
|
||||
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`.
|
||||
This document describes how to set up your development environment to build and test Angular 2 JS version.
|
||||
It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||
|
||||
* [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)
|
||||
* [Build commands](#build-commands)
|
||||
* [Installing NPM Modules](#installing-npm-modules)
|
||||
* [Building](#building)
|
||||
* [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)
|
||||
|
||||
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
if you'd like to contribute to Angular.
|
||||
@ -23,30 +17,19 @@ if you'd like to contribute to Angular.
|
||||
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.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).
|
||||
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)); [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), (version `>=4.2.1 <5`) which is used to run a development web server,
|
||||
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) 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.7 <3.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
(version `>=3.5.3 <4.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/).
|
||||
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||
to run Bazel and execute the selenium standalone server for e2e testing. JDK version 8 is required.
|
||||
|
||||
* [Bazel](https://bazel.io) is used to build and test Angular packages.
|
||||
|
||||
## Getting the Sources
|
||||
|
||||
@ -69,288 +52,110 @@ cd angular
|
||||
# Add the main Angular repository as an upstream remote to your repository:
|
||||
git remote add upstream https://github.com/angular/angular.git
|
||||
```
|
||||
## Installing NPM Modules
|
||||
|
||||
## Environment Variable Setup
|
||||
|
||||
Define the environment variables listed below. These are mainly needed for the testing. The
|
||||
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
|
||||
your favorite shell.
|
||||
|
||||
Examples given below of possible values for initializing the environment variables assume **Mac OS
|
||||
X** and that you have installed the Dart Editor in the directory named by
|
||||
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
|
||||
|
||||
```shell
|
||||
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
|
||||
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
|
||||
```
|
||||
|
||||
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
|
||||
[here](https://www.dartlang.org/tools/pub/installing.html)):
|
||||
|
||||
```shell
|
||||
# DART_SDK: path to a Dart SDK directory
|
||||
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
|
||||
|
||||
# Update PATH to include the Dart SDK bin directory
|
||||
PATH+=":$DART_SDK/bin"
|
||||
```
|
||||
|
||||
And specify where the pub’s dependencies are downloaded. By default, this directory is located under .pub_cache
|
||||
in your home directory (on Mac and Linux), or in AppData\Roaming\Pub\Cache (on Windows).
|
||||
|
||||
```shell
|
||||
# PUB_CACHE: location of pub dependencies
|
||||
export PUB_CACHE="/Users/<user>/.pub-cache"
|
||||
```
|
||||
|
||||
## Installing NPM Modules and Dart Packages
|
||||
|
||||
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
|
||||
Next, install the JavaScript modules needed to build and test Angular:
|
||||
|
||||
```shell
|
||||
# Install Angular project dependencies (package.json)
|
||||
npm install
|
||||
```
|
||||
|
||||
**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 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, 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:
|
||||
## Windows only
|
||||
|
||||
In order to create the right symlinks, run **as administrator**:
|
||||
```shell
|
||||
$(npm bin)/gulp build
|
||||
./scripts/windows/create-symlinks.sh
|
||||
```
|
||||
|
||||
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`.
|
||||
Before submitting a PR, do not forget to remove them:
|
||||
```shell
|
||||
./scripts/windows/remove-symlinks.sh
|
||||
```
|
||||
|
||||
You can selectively build either the JS or Dart versions as follows:
|
||||
## Building
|
||||
|
||||
* `$(npm bin)/gulp build.js`
|
||||
* `$(npm bin)/gulp build.dart`
|
||||
|
||||
To clean out the `dist` folder, run:
|
||||
To build Angular run:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp clean
|
||||
bazel build :all_packages
|
||||
```
|
||||
|
||||
To build individual modules, use `:<package_name>_package`, e.g.
|
||||
`:core_package`.
|
||||
|
||||
* The npm build artifacts are put in the bazel-bin/ folder as tarballs.
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
### Full test suite
|
||||
|
||||
* `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).
|
||||
* `$(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:
|
||||
|
||||
* `$(npm bin)/gulp test.unit.js/ci`
|
||||
* `$(npm bin)/gulp test.unit.cjs/ci`
|
||||
* `$(npm bin)/gulp test.unit.dart/ci`
|
||||
|
||||
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**: 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.
|
||||
|
||||
**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.
|
||||
|
||||
### Unit tests with Sauce Labs or Browser Stack
|
||||
|
||||
First, in a terminal, create a tunnel with [Sauce Connect](https://docs.saucelabs.com/reference/sauce-connect/) or [Browser Stack Local](https://www.browserstack.com/local-testing#command-line), and valid credentials.
|
||||
|
||||
Then, in another terminal:
|
||||
- Define the credentials as environment variables, e.g.:
|
||||
```
|
||||
export SAUCE_USERNAME='my_user'; export SAUCE_ACCESS_KEY='my_key';
|
||||
export BROWSER_STACK_USERNAME='my_user'; export BROWSER_STACK_ACCESS_KEY='my_key';
|
||||
```
|
||||
- Then run `gulp test.unit.js.(saucelabs|browserstack) --browsers=option1,option2,..,optionN`
|
||||
The options are any mix of browsers and aliases which are defined in the [browser-providers.conf.js](https://github.com/angular/angular/blob/master/browser-providers.conf.js) file.
|
||||
They are case insensitive, and the `SL_` or `BS_` prefix must not be added for browsers.
|
||||
|
||||
Some examples of commands:
|
||||
```
|
||||
gulp test.unit.js.saucelabs --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
||||
gulp test.unit.js.browserstack --browsers=Safari,IE //run in Browser Stack with Safari 7, Safari 8, Safari 9, IE 9, IE 10 and IE 11
|
||||
gulp test.unit.js.saucelabs --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
||||
```
|
||||
|
||||
### 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.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.
|
||||
|
||||
Angular specific command line options when running protractor:
|
||||
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
### 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.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
|
||||
|
||||
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.
|
||||
|
||||
* Use `$(npm bin)/clang-format -i [file name]` to format a file (or multiple).
|
||||
* 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: `$ProjectFileDir$/node_modules/.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`:
|
||||
To run tests:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp docs/angular.io
|
||||
$ bazel test :jasmine_tests # Run all angular tests on node
|
||||
|
||||
$ bazel test :karma_test # Run all angular tests except router in browser
|
||||
$ bazel test :router_karma_test # Run angular router tests in browser
|
||||
|
||||
|
||||
# If you are on Linux, you will need to add `--test_env=DISPLAY` at the end of the Bazel command so that
|
||||
# Chrome will be able to launch successfully. For example:
|
||||
$ bazel test :karma_test --test_env=DISPLAY
|
||||
|
||||
# Alternatively, use `bazel run` instead:
|
||||
$ bazel run :karma_test
|
||||
```
|
||||
|
||||
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.
|
||||
You should execute the 2 test suites before submitting a PR to github.
|
||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||
|
||||
## Project Information
|
||||
- CircleCI fails if your code is not formatted properly,
|
||||
- Travis CI fails if any of the test suite describe above fails.
|
||||
|
||||
### Folder structure
|
||||
## Update the public API tests
|
||||
|
||||
* `modules/*`: modules that will be loaded in the browser
|
||||
* `tools/*`: tools that are needed to build Angular
|
||||
* `dist/*`: build files are placed here.
|
||||
If you happen to modify the public API of Angular, API golden files must be updated using:
|
||||
|
||||
### File suffixes
|
||||
``` shell
|
||||
$ bazel run :public_api
|
||||
```
|
||||
|
||||
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
|
||||
* `*.dart`: Dart files that don't get transpiled
|
||||
Note: The command `bazel test :public_api_test` fails when the API doesn't match the golden files.
|
||||
|
||||
## CI using Travis
|
||||
## Formatting your source code
|
||||
|
||||
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
||||
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
|
||||
## Transforming Dart code
|
||||
You can automatically format your code by running:
|
||||
|
||||
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
|
||||
``` shell
|
||||
$ gulp format
|
||||
```
|
||||
|
||||
## Debugging
|
||||
## Incremental development
|
||||
|
||||
### Debug the transpiler
|
||||
To watch files and automatically rebuild, use `./ibazel` instead of the `bazel` command. For example:
|
||||
|
||||
If you need to debug the transpiler:
|
||||
``` shell
|
||||
$ ./ibazel build :core # Automatically recompiles core in ES5
|
||||
|
||||
- add a `debugger;` statement in the transpiler code,
|
||||
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
|
||||
debugger
|
||||
- press "c" to execute the program until you reach the `debugger;` statement,
|
||||
- you can then type "repl" to enter the REPL and inspect variables in the context.
|
||||
$ ./ibazel build :jasmine_tests # Automatically reruns all tests on node
|
||||
```
|
||||
|
||||
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
|
||||
To debug tests:
|
||||
|
||||
Notes:
|
||||
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
|
||||
code you want to debug (the former will process the "modules" folder while the later processes
|
||||
the transpiler specs).
|
||||
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
|
||||
the developer tools are opened in the browser running Karma.
|
||||
``` shell
|
||||
$ ./bazel-run.sh run :core_test --node_options=debug
|
||||
# Runs the core test suite in `node debug`
|
||||
$ ./ibazel run :karma_test_local # Runs karma in watch mode
|
||||
$ ./ibazel run :router_karma_test_local
|
||||
|
||||
### Debug the tests
|
||||
$ ./ibazel run :playground_test -- --serve-only
|
||||
# Runs the development server for e2e tests
|
||||
```
|
||||
|
||||
If you need to debug the tests:
|
||||
* `./bazel-run.sh` is a variant of `bazel run` that connects stdin, and can be used for running
|
||||
node in debug mode.
|
||||
|
||||
- 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 DevTools and press F5,
|
||||
- the execution halts at the `debugger;` statement
|
||||
|
||||
**Note (WebStorm users)**:
|
||||
|
||||
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.
|
||||
See [Bazel README](https://github.com/angular/angular/blob/master/build_defs/README.md) for inner workings
|
||||
of the build system.
|
||||
|
215
LICENSE
215
LICENSE
@ -1,202 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
The MIT License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2014-2016 Google, Inc. http://angular.io
|
||||
|
||||
1. Definitions.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
30
README.md
30
README.md
@ -1,31 +1,27 @@
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://circleci.com/gh/angular/angular/tree/master)
|
||||
[](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)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
|
||||
Angular
|
||||
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.
|
||||
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
||||
|
||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||
|
||||
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).
|
||||
Angular 2 is currently in **Release Candidate**.
|
||||
|
||||
## Quickstart
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Setup & Install Angular 2
|
||||
|
||||
Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
|
||||
## Want to help?
|
||||
|
||||
@ -36,8 +32,8 @@ guidelines for [contributing][contributing] and then check out one of our issues
|
||||
[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/
|
||||
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
|
||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
[ng2dart]: https://github.com/dart-lang/angular2
|
||||
|
140
TOOLS.md
140
TOOLS.md
@ -1,4 +1,140 @@
|
||||
# Developer Tools for Angular 2
|
||||
|
||||
- [JavaScript](TOOLS_JS.md)
|
||||
- [Dart](TOOLS_DART.md)
|
||||
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 {enableDebugTools} from '@angular/platform-browser';
|
||||
|
||||
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
|
||||
@Component({
|
||||
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).
|
||||
|
376
TOOLS_DART.md
376
TOOLS_DART.md
@ -1,376 +0,0 @@
|
||||
# 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:
|
||||
|
||||
```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.
|
||||
|
||||
#### Use --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/).
|
||||
|
||||
#### Use 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.
|
||||
|
||||
#### Find unused reflection data
|
||||
|
||||
Your app might have types that are annotated with `@Component` or `@Injectable`
|
||||
but never used.
|
||||
To find these unused types, use `reflector.trackUsage()` and then,
|
||||
after exercising your app, `reflector.listUnusedKeys()`.
|
||||
For example:
|
||||
|
||||
```
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
...
|
||||
main() async {
|
||||
reflector.trackUsage();
|
||||
await bootstrap(AppComponent);
|
||||
print('Unused keys: ${reflector.listUnusedKeys()}');
|
||||
}
|
||||
```
|
||||
|
||||
When you run that code (in Dartium or another browser),
|
||||
you'll see a list of types that Angular _can_ inject but hasn't needed to.
|
||||
Consider removing those types or their `@Component`/`@Injectable` annotation
|
||||
to decrease your app's code size.
|
||||
|
||||
Three conditions must be true for `listUnusedKeys()` to return helpful data:
|
||||
|
||||
1. The angular2 transformer must run on the app.
|
||||
2. If you're running a JavaScript version of the app,
|
||||
the app must not be minified, so that the names are readable.
|
||||
3. You must exercise your app in as many ways as possible
|
||||
before calling `listUnusedKeys()`.
|
||||
Otherwise, you might get false positives:
|
||||
keys that haven't been used only because you didn't exercise
|
||||
the relevant feature of the app.
|
||||
|
||||
To run the angular2 transformer, first specify it in `pubspec.yaml`:
|
||||
|
||||
```
|
||||
name: hello_world
|
||||
...
|
||||
transformers:
|
||||
- angular2:
|
||||
entry_points: web/main.dart
|
||||
```
|
||||
|
||||
Then use pub to run the transformer. If you use `pub serve`,
|
||||
it provides both Dart and unminified (by default) JavaScript versions.
|
||||
If you want to serve actual files, then use `pub build` in debug mode
|
||||
to generate Dart and unminified JavaScript files:
|
||||
`pub build --mode=debug`.
|
||||
|
||||
The `reflector.trackUsage()` method makes Angular track the reflection
|
||||
information used by the app. Reflection information (`ReflectionInfo`) is a data
|
||||
structure that stores information that Angular uses for locating DI factories
|
||||
and for generating change detectors and other code related to a
|
||||
given type.
|
||||
|
||||
#### 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, you need to find out why. This tool helps by measuring the average
|
||||
speed of _change detection_, a phase in Angular's
|
||||
lifecycle that detects changes in values that are bound to the UI.
|
||||
Janky UI updates can result from slowness either in _computing_ the changes or
|
||||
in _applying_ those changes to the UI.
|
||||
|
||||
For your app to be performant, the process of _computing_ changes must be very
|
||||
fast—preferably **under 3 milliseconds**.
|
||||
Fast change computation leaves room for
|
||||
the application logic, UI updates, and browser rendering pipeline
|
||||
to fit within a 16 ms frame (assuming a target frame rate of 60 FPS).
|
||||
|
||||
The 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
|
||||
(in milliseconds) to perform a single cycle of change detection and
|
||||
prints that 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
|
||||
|
||||
Before running the profiler, enable the debugging tools
|
||||
and put the app into the state you want to measure:
|
||||
|
||||
1. If you haven't already done so,
|
||||
[enable the debugging tools](#enabling-the-debugging-tools).
|
||||
2. Navigate the app to a screen whose performance you want to profile.
|
||||
3. Make sure the screen is in a state that you want to measure.
|
||||
For example, you might want to profile the screen several times,
|
||||
with different amounts and kinds of data.
|
||||
|
||||
To run the profiler, enter the following in the dev console:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The results are visible in 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.
|
||||
|
||||
#### 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
140
TOOLS_JS.md
@ -1,140 +0,0 @@
|
||||
# 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 {enableDebugTools} from 'angular2/platform/browser';
|
||||
|
||||
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).
|
7
WORKSPACE
Normal file
7
WORKSPACE
Normal file
@ -0,0 +1,7 @@
|
||||
workspace(name="angular")
|
||||
|
||||
load("//build_defs:nodejs_workspace.bzl", "nodejs_workspace")
|
||||
|
||||
nodejs_workspace(
|
||||
name = "nodejs",
|
||||
)
|
49
bazel-run.sh
Executable file
49
bazel-run.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copied from https://github.com/bazelbuild/bazel/blob/master/scripts/bazel-run.sh
|
||||
|
||||
#
|
||||
# Copyright 2016 The Bazel Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
USAGE='bazel-run.sh [<bazel option>...] <target> [ -- [<target option>]... ]'
|
||||
DESCRIPTION='
|
||||
Builds and runs the command generated by "bazel run" in the calling
|
||||
terminal. The command is run as a grandchild of the current shell,
|
||||
not from the Bazel server. Therefore, the program will have a controlling terminal, and
|
||||
the Bazel lock is released before running the command.'
|
||||
|
||||
function usage() {
|
||||
echo "$USAGE" "$DESCRIPTION" >&2
|
||||
}
|
||||
|
||||
function die() {
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
rm "$runcmd"
|
||||
}
|
||||
|
||||
[ $# -gt 0 ] || { usage; exit 1; }
|
||||
|
||||
runcmd="$(mktemp /tmp/bazel-run.XXXXXX)" || die "Could not create tmp file"
|
||||
trap "cleanup" EXIT
|
||||
|
||||
bazel run --script_path="$runcmd" "$@" || exit $?
|
||||
[ -x "$runcmd" ] || die "File $runcmd not executable"
|
||||
|
||||
"$runcmd"
|
||||
|
@ -1,3 +1,33 @@
|
||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
|
||||
// If the target is set to null, then the browser is not run anywhere during CI.
|
||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
||||
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
||||
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
||||
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
||||
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
var customLaunchers = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
@ -8,7 +38,7 @@ var customLaunchers = {
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '46'
|
||||
version: '52'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -23,7 +53,7 @@ var customLaunchers = {
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '42'
|
||||
version: '46'
|
||||
},
|
||||
'SL_FIREFOXBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -39,15 +69,15 @@ var customLaunchers = {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
version: '7.0'
|
||||
},
|
||||
'SL_SAFARI8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8'
|
||||
version: '8.0'
|
||||
},
|
||||
'SL_SAFARI9.0': {
|
||||
'SL_SAFARI9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11',
|
||||
@ -69,7 +99,7 @@ var customLaunchers = {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '9.1'
|
||||
version: '9.3'
|
||||
},
|
||||
'SL_IE9': {
|
||||
base: 'SauceLabs',
|
||||
@ -91,9 +121,9 @@ var customLaunchers = {
|
||||
},
|
||||
'SL_EDGE': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'microsoftedge',
|
||||
browserName: 'MicrosoftEdge',
|
||||
platform: 'Windows 10',
|
||||
version: '20.10240'
|
||||
version: '13.10586'
|
||||
},
|
||||
'SL_ANDROID4.1': {
|
||||
base: 'SauceLabs',
|
||||
@ -119,7 +149,7 @@ var customLaunchers = {
|
||||
platform: 'Linux',
|
||||
version: '4.4'
|
||||
},
|
||||
'SL_ANDROID5.1': {
|
||||
'SL_ANDROID5': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
@ -172,7 +202,7 @@ var customLaunchers = {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6S',
|
||||
os: 'ios',
|
||||
os_version: '9.0'
|
||||
os_version: '9.1'
|
||||
},
|
||||
'BS_IE9': {
|
||||
base: 'BrowserStack',
|
||||
@ -239,21 +269,18 @@ var customLaunchers = {
|
||||
}
|
||||
};
|
||||
|
||||
// iOS9 deactivated as not reliable in both providers
|
||||
// TODO(mlaval): reactivate after https://github.com/angular/angular/issues/5408
|
||||
|
||||
var sauceAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9.0'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1'],
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9.0'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||
'CI': ['SL_CHROME',' SL_FIREFOX', 'SL_CHROMEDEV', 'SL_FIREFOXBETA', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE',
|
||||
'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1']
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false)
|
||||
};
|
||||
|
||||
var browserstackAliases = {
|
||||
@ -264,16 +291,23 @@ var browserstackAliases = {
|
||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'CI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_IOS7', 'BS_IOS8', 'BS_WINDOWSPHONE']
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
customLaunchers: customLaunchers,
|
||||
sauceAliases: sauceAliases,
|
||||
browserstackAliases: browserstackAliases
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
|
||||
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => {
|
||||
return target + '_' + item.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
110
build.sh
Executable file
110
build.sh
Executable file
@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
|
||||
|
||||
rm -rf ./dist/all/
|
||||
mkdir -p ./dist/all/
|
||||
|
||||
TSCONFIG=./tools/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
$(npm bin)/tsc -p ${TSCONFIG}
|
||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||
|
||||
echo "====== Copying files needed for e2e tests ====="
|
||||
cp -r ./modules/playground ./dist/all/
|
||||
cp -r ./modules/playground/favicon.ico ./dist/
|
||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||
mkdir ./dist/all/playground/vendor
|
||||
cd ./dist/all/playground/vendor
|
||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
cd -
|
||||
|
||||
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
# compile ts code
|
||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
$TSC -p modules/tsconfig.json
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
|
||||
for PACKAGE in \
|
||||
core \
|
||||
compiler \
|
||||
common \
|
||||
forms \
|
||||
platform-browser \
|
||||
platform-browser-dynamic \
|
||||
platform-server \
|
||||
http \
|
||||
router \
|
||||
upgrade \
|
||||
compiler-cli
|
||||
do
|
||||
SRCDIR=./modules/@angular/${PACKAGE}
|
||||
DESTDIR=./dist/packages-dist/${PACKAGE}
|
||||
UMD_ES6_PATH=${DESTDIR}/esm/${PACKAGE}.umd.js
|
||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-es5.json
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
|
||||
|
||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
else
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
fi
|
||||
|
||||
if [[ ${PACKAGE} != compiler-cli ]]; then
|
||||
|
||||
echo "====== (esm)COMPILING: $TSC -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-es2015.json
|
||||
|
||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||
mkdir ${DESTDIR}/bundles
|
||||
|
||||
(
|
||||
cd ${SRCDIR}
|
||||
echo "..." # here just to have grep match something and not exit with 1
|
||||
../../../node_modules/.bin/rollup -c rollup.config.js
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
$(npm bin)/tsc \
|
||||
--out ${UMD_ES5_PATH} \
|
||||
--target es5 \
|
||||
--lib "es6,dom" \
|
||||
--allowJs \
|
||||
${UMD_ES6_PATH}
|
||||
|
||||
rm ${UMD_ES6_PATH}
|
||||
|
||||
cat ./modules/@angular/license-banner.txt > ${UMD_ES5_PATH}.tmp
|
||||
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
|
||||
$(npm bin)/uglifyjs -c --screw-ie8 -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
fi
|
||||
done
|
||||
|
||||
echo "====== COMPILING: \$(npm bin)/tsc -p benchpress/tsconfig.json ====="
|
||||
$(npm bin)/tsc -p ./modules/benchpress/tsconfig.json
|
9
build_defs/BUILD
Normal file
9
build_defs/BUILD
Normal file
@ -0,0 +1,9 @@
|
||||
exports_files([
|
||||
"nodejs_launcher_template.sh",
|
||||
"jasmine_launcher_template.sh",
|
||||
"karma_launcher_template.sh",
|
||||
"rollup_config_template.js",
|
||||
"protractor_launcher_template.sh",
|
||||
"ts_api_guardian_launcher_template.sh",
|
||||
"check_cycle.js",
|
||||
])
|
126
build_defs/bundle.bzl
Normal file
126
build_defs/bundle.bzl
Normal file
@ -0,0 +1,126 @@
|
||||
load("//build_defs:utils.bzl", "join_paths", "pick_file")
|
||||
|
||||
|
||||
def _js_bundle_impl(ctx):
|
||||
"""
|
||||
Rule for creating a minified bundle for JavaScript web libraries. This
|
||||
includes tree-shaking with Rollup.js, down-transpiling to ES5 with TypeScript,
|
||||
and minifying with UglifyJS.
|
||||
|
||||
Args:
|
||||
srcs: Target containing the source library.
|
||||
deps: JavaScript targets which the tests depend on.
|
||||
rollup_config: Required. Rollup.js config file to use.
|
||||
entry_point: Path to the entrypoint file for tree-shaking, relative to
|
||||
the package. The entry_point should be inside the first "srcs" target.
|
||||
output: Filename of the gen .js file, relative to the package. For
|
||||
example, if you specify bundle.js, bundle.js and bundle.min.js will be
|
||||
created.
|
||||
banner: File to prepend to the gen files. Useful for e.g. copyright
|
||||
banners.
|
||||
"""
|
||||
output = (ctx.attr.output or ctx.label.name + ".js")
|
||||
output_base = output[:output.rfind(".")]
|
||||
|
||||
# gen_esm_js = ctx.new_file(output_base + ".esm.js")
|
||||
# gen_esm_js_map = ctx.new_file(output_base + ".esm.js.map")
|
||||
gen_js = ctx.new_file(output_base + ".js")
|
||||
gen_js_map = ctx.new_file(output_base + ".js.map")
|
||||
gen_min_js = ctx.new_file(output_base + ".min.js")
|
||||
gen_min_js_map = ctx.new_file(output_base + ".min.js.map")
|
||||
|
||||
config_file = ctx.new_file("%s_rollup.config.js" % ctx.label.name)
|
||||
|
||||
config_to_workspace = "/".join(
|
||||
[".." for x in ctx.configuration.bin_dir.path.split("/") if x] +
|
||||
[".." for x in ctx.label.package.split("/") if x])
|
||||
|
||||
esm_inputs = []
|
||||
for src in ctx.attr.srcs:
|
||||
esm_inputs += src.javascript_esm.files + src.javascript_esm.source_maps
|
||||
|
||||
main_src = ctx.attr.srcs[0]
|
||||
entry_point = pick_file(esm_inputs, main_src.label, ctx.attr.entry_point)
|
||||
|
||||
ctx.template_action(
|
||||
template = ctx.file._rollup_config_template,
|
||||
output = config_file,
|
||||
substitutions = {
|
||||
"{{base_config}}": join_paths(config_to_workspace, ctx.file.rollup_config.path),
|
||||
# Unlike tsc, rollup does not resolve paths relative to
|
||||
# rollup.config.js.
|
||||
"{{prefixes}}": "\"\", \"{}\", \"{}\"".format(
|
||||
ctx.configuration.bin_dir.path, ctx.configuration.genfiles_dir.path),
|
||||
"{{entry}}": "./" + entry_point.path,
|
||||
"{{dest}}": gen_js.path,
|
||||
"{{banner}}": (ctx.file.banner.path if ctx.attr.banner else ""),
|
||||
},
|
||||
)
|
||||
|
||||
ctx.action(
|
||||
progress_message = "Tree shaking %s" % ctx,
|
||||
inputs = esm_inputs + ctx.files.rollup_config + ctx.files.banner + [config_file],
|
||||
outputs = [gen_js, gen_js_map],
|
||||
executable = ctx.executable._rollup,
|
||||
arguments = ["-c", config_file.path],
|
||||
)
|
||||
|
||||
# tsc_cmd = [ctx.executable._tsc.path, "--noResolve", "--target", "es5", "--allowJs", "--typeRoots",
|
||||
# "[]", "--sourceMap", "--inlineSources", "--outFile", gen_js.path, gen_esm_js.path]
|
||||
# ctx.action(
|
||||
# progress_message = "Compiling ES6 %s" % ctx,
|
||||
# inputs = [gen_esm_js, gen_esm_js_map] + list(ctx.attr._tsc.default_runfiles.files),
|
||||
# outputs = [gen_js, gen_js_map],
|
||||
# executable = ctx.executable._flatten_sourcemap,
|
||||
# arguments = [gen_js.path, "--"] + tsc_cmd,
|
||||
# )
|
||||
|
||||
ctx.action(
|
||||
progress_message = "Minifying bundle of %s" % ctx,
|
||||
inputs = [gen_js, gen_js_map] + ctx.files.banner,
|
||||
outputs = [gen_min_js, gen_min_js_map],
|
||||
executable = ctx.executable._uglifyjs,
|
||||
arguments = (
|
||||
(["--preamble-file", ctx.file.banner.path] if ctx.attr.banner else []) +
|
||||
["--compress", "--screw-ie8", "--in-source-map", gen_js_map.path,
|
||||
"--source-map-include-sources", "--output", gen_min_js.path, "--source-map-url",
|
||||
gen_min_js_map.basename, "--source-map", gen_min_js_map.path, gen_js.path]
|
||||
),
|
||||
)
|
||||
|
||||
files = [gen_js, gen_js_map, gen_min_js, gen_min_js_map]
|
||||
|
||||
return struct(
|
||||
files = set(files),
|
||||
runfiles = ctx.runfiles(
|
||||
files = files,
|
||||
# TODO: Investigate why setting collect_data = True will serve the
|
||||
# source *.js files.
|
||||
collect_data = False,
|
||||
collect_default = False,
|
||||
),
|
||||
)
|
||||
|
||||
js_bundle = rule(
|
||||
implementation = _js_bundle_impl,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(allow_files=True, mandatory=True),
|
||||
"deps": attr.label_list(),
|
||||
"rollup_config": attr.label(allow_files=True, single_file=True, mandatory=True),
|
||||
"entry_point": attr.string(default="index.js"),
|
||||
"output": attr.string(),
|
||||
"banner": attr.label(allow_files=True, single_file=True),
|
||||
|
||||
"_rollup": attr.label(default=Label("//:rollup_bin"), executable=True),
|
||||
"_uglifyjs": attr.label(
|
||||
default=Label("//build_defs/tools:uglifyjs_wrapped"), executable=True),
|
||||
"_tsc": attr.label(default=Label("//:tsc_bin"), executable=True),
|
||||
"_flatten_sourcemap": attr.label(
|
||||
default=Label("//build_defs/tools:flatten_sourcemap"), executable=True),
|
||||
"_rollup_config_template": attr.label(
|
||||
default = Label("//build_defs:rollup_config_template.js"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
},
|
||||
)
|
16
build_defs/check_cycle.js
Normal file
16
build_defs/check_cycle.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const madge = require('madge');
|
||||
|
||||
const dependencyObject = madge([process.env.RUNFILES + '/modules/'], {
|
||||
format: 'cjs',
|
||||
extensions: ['.js'],
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); }
|
||||
});
|
||||
|
||||
const circularDependencies = dependencyObject.circular().getArray();
|
||||
if (circularDependencies.length > 0) {
|
||||
console.log('Found circular dependencies!');
|
||||
console.log(circularDependencies);
|
||||
process.exit(1);
|
||||
}
|
69
build_defs/jasmine.bzl
Normal file
69
build_defs/jasmine.bzl
Normal file
@ -0,0 +1,69 @@
|
||||
load("//build_defs:utils.bzl", "pseudo_json_encode")
|
||||
|
||||
|
||||
def _jasmine_node_test_impl(ctx):
|
||||
"""
|
||||
Rule for running Jasmine tests on NodeJS.
|
||||
|
||||
Args:
|
||||
srcs: The targets containing the spec files.
|
||||
deps: JavaScript targets which the tests depend on.
|
||||
data: Data files which the tests depend on.
|
||||
helpers: List of JavaScript targets to be loaded as helpers.
|
||||
"""
|
||||
# This rule works by creating a Jasmine config file with a list of helper and
|
||||
# spec files, then creating a launcher shell script that runs Jasmine CLI with
|
||||
# the said config file.
|
||||
config_file = ctx.new_file("%s_jasmine.json" % ctx.label.name)
|
||||
|
||||
ctx.file_action(
|
||||
output = config_file,
|
||||
content = pseudo_json_encode({
|
||||
"spec_dir": ".",
|
||||
"spec_files": [f.short_path for f in ctx.files.srcs if f.short_path.endswith(".js")],
|
||||
"helpers": [f.short_path for f in ctx.files.helpers if f.short_path.endswith(".js")],
|
||||
}),
|
||||
)
|
||||
|
||||
ctx.template_action(
|
||||
template = ctx.file._launcher_template,
|
||||
output = ctx.outputs.executable,
|
||||
substitutions = {
|
||||
"{{jasmine}}": ctx.executable._jasmine.short_path,
|
||||
"{{config}}": config_file.short_path,
|
||||
},
|
||||
executable = True,
|
||||
)
|
||||
|
||||
transitive_files = set(ctx.attr._jasmine.default_runfiles.files)
|
||||
for helper in ctx.attr.helpers:
|
||||
transitive_files += set(helper.default_runfiles.files)
|
||||
|
||||
return struct(
|
||||
files = set([ctx.outputs.executable]),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs + ctx.files._jasmine + [config_file] + ctx.files.helpers,
|
||||
transitive_files = transitive_files,
|
||||
collect_data = True,
|
||||
collect_default = True,
|
||||
),
|
||||
)
|
||||
|
||||
jasmine_node_test = rule(
|
||||
implementation = _jasmine_node_test_impl,
|
||||
executable = True,
|
||||
test = True,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(allow_files=True),
|
||||
"deps": attr.label_list(),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
"helpers": attr.label_list(default=[], allow_files=True),
|
||||
|
||||
"_jasmine": attr.label(default=Label("//:jasmine_bin"), executable=True),
|
||||
"_launcher_template": attr.label(
|
||||
default = Label("//build_defs:jasmine_launcher_template.sh"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
},
|
||||
)
|
21
build_defs/jasmine_launcher_template.sh
Normal file
21
build_defs/jasmine_launcher_template.sh
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z "${RUNFILES}" ]]; then
|
||||
case "${0}" in
|
||||
/*) self="${0}" ;;
|
||||
*) self="${PWD}/${0}" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "${TEST_SRCDIR}" ]]; then
|
||||
export RUNFILES="${TEST_SRCDIR}/angular"
|
||||
elif [[ -d "${self}.runfiles" ]]; then
|
||||
export RUNFILES="${self}.runfiles/angular"
|
||||
else
|
||||
echo "Runfiles directory not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "${RUNFILES}" && "${RUNFILES}/{{jasmine}}" --color "JASMINE_CONFIG_PATH=${RUNFILES}/{{config}}" "$@"
|
75
build_defs/karma.bzl
Normal file
75
build_defs/karma.bzl
Normal file
@ -0,0 +1,75 @@
|
||||
_KARMA_TEST_ATTRS = {
|
||||
"srcs": attr.label_list(allow_files=True),
|
||||
"deps": attr.label_list(),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
"config": attr.label(allow_files=True, single_file=True, mandatory=True),
|
||||
|
||||
"_karma": attr.label(default=Label("//:karma_bin"), executable=True),
|
||||
"_launcher_template": attr.label(
|
||||
default = Label("//build_defs:karma_launcher_template.sh"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
}
|
||||
|
||||
def _karma_test_impl(ctx, karma_args="--single-run"):
|
||||
"""
|
||||
Rule for running Karma tests.
|
||||
|
||||
Args:
|
||||
srcs: The targets containing the spec files.
|
||||
deps: JavaScript targets which the tests depend on.
|
||||
data: Data files which the tests depend on.
|
||||
config: Required. Karma config file to use.
|
||||
|
||||
Due to the complexity of Karma config files, this rule does not do the heavy
|
||||
lifting of creating that config file. Instead, the user has to point to the
|
||||
right files. Files specified in Karma must be a subset of files depended upon
|
||||
in the karma_test definition.
|
||||
|
||||
This rule additionally creates a <name>_local target, which runs Karma in
|
||||
watch mode. This can be combined with ibazel for incremental development.
|
||||
"""
|
||||
ctx.template_action(
|
||||
template = ctx.file._launcher_template,
|
||||
output = ctx.outputs.executable,
|
||||
substitutions = {
|
||||
"{{karma}}": ctx.executable._karma.short_path,
|
||||
"{{config}}": ctx.file.config.short_path,
|
||||
"{{args}}": karma_args,
|
||||
},
|
||||
executable = True,
|
||||
)
|
||||
|
||||
return struct(
|
||||
files = set([ctx.outputs.executable]),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs + ctx.files._karma + ctx.files.config,
|
||||
transitive_files = set(ctx.attr._karma.default_runfiles.files),
|
||||
collect_data = True,
|
||||
collect_default = True,
|
||||
),
|
||||
)
|
||||
|
||||
_karma_test = rule(
|
||||
implementation = _karma_test_impl,
|
||||
executable = True,
|
||||
test = True,
|
||||
attrs = _KARMA_TEST_ATTRS,
|
||||
)
|
||||
|
||||
def _karma_test_local_impl(ctx):
|
||||
return _karma_test_impl(ctx, karma_args = "")
|
||||
|
||||
_karma_test_local = rule(
|
||||
implementation = _karma_test_local_impl,
|
||||
executable = True,
|
||||
attrs = _KARMA_TEST_ATTRS,
|
||||
)
|
||||
|
||||
def karma_test(*, name, timeout=None, size=None, flaky=None, shard_count=None, local=None,
|
||||
tags=None, **kwargs):
|
||||
tags = tags or []
|
||||
_karma_test(name=name, timeout=timeout, size=size, flaky=flaky, shard_count=shard_count,
|
||||
local=local, tags=tags, **kwargs)
|
||||
_karma_test_local(name=name + "_local", tags=tags + ["ibazel_notify_changes"], **kwargs)
|
21
build_defs/karma_launcher_template.sh
Normal file
21
build_defs/karma_launcher_template.sh
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z "${RUNFILES}" ]]; then
|
||||
case "${0}" in
|
||||
/*) self="${0}" ;;
|
||||
*) self="${PWD}/${0}" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "${TEST_SRCDIR}" ]]; then
|
||||
export RUNFILES="${TEST_SRCDIR}/angular"
|
||||
elif [[ -d "${self}.runfiles" ]]; then
|
||||
export RUNFILES="${self}.runfiles/angular"
|
||||
else
|
||||
echo "Runfiles directory not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "${RUNFILES}" && "${RUNFILES}/{{karma}}" start "${RUNFILES}/{{config}}" {{args}} "$@"
|
133
build_defs/node_modules_index.bzl
Normal file
133
build_defs/node_modules_index.bzl
Normal file
File diff suppressed because one or more lines are too long
221
build_defs/node_modules_indexer.js
Normal file
221
build_defs/node_modules_indexer.js
Normal file
@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function printUsageAndExit() {
|
||||
console.error(`Usage: ${process.argv[0]} ${process.argv[1]}
|
||||
<package root location> [output file] [--verify]
|
||||
|
||||
For each dependency in package.json, resolve the dependencies required by that
|
||||
package and serialize the dependency graph into a Bazel macro using
|
||||
nodejs_module, nodejs_binary and ts_ext_library.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const root = process.argv[2];
|
||||
if (!root) {
|
||||
printUsageAndExit();
|
||||
}
|
||||
|
||||
const nodeModules = path.join(root, 'node_modules');
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json')));
|
||||
// Only create targets for direct dependencies
|
||||
const targets =
|
||||
Object.keys(readDependencies(packageJson, ['dependencies', 'devDependencies'])).sort();
|
||||
|
||||
const packages = traceNodeModules(nodeModules);
|
||||
const packageMap = {};
|
||||
for (const pkg of packages) {
|
||||
packageMap[pkg] = tracePackage(path.join(nodeModules, pkg));
|
||||
}
|
||||
|
||||
const targetDefs = targets.map(target => {
|
||||
// Find out the set of dependencies and classify them into targets (deps)
|
||||
// and non-targets (srcs)
|
||||
return {
|
||||
name: target,
|
||||
srcs: Object.keys(collectSrcs(target)).sort().map(src => 'node_modules/' + src),
|
||||
deps: Object.keys(packageMap[target].deps).sort().filter(dep => targets.indexOf(dep) !== -1),
|
||||
typings: packageMap[target].typings,
|
||||
};
|
||||
});
|
||||
|
||||
let output = `load("//build_defs:nodejs.bzl", "nodejs_module", "nodejs_binary")
|
||||
load("//build_defs:typescript.bzl", "ts_ext_library")
|
||||
|
||||
def node_modules_index(glob):
|
||||
`;
|
||||
|
||||
for (const def of targetDefs) {
|
||||
let fn;
|
||||
const args = {
|
||||
name: `"${escapeName(def.name)}"`,
|
||||
srcs: `[${def.srcs.map(src => `"${src}"`).join(', ')}]`,
|
||||
};
|
||||
if (!def.typings) {
|
||||
fn = 'nodejs_module';
|
||||
args.deps = `[${def.deps.map(dep => `":${escapeName(dep)}"`).join(', ')}]`;
|
||||
} else {
|
||||
fn = 'ts_ext_library';
|
||||
|
||||
// ts_ext_library "deps" must be typescript targets
|
||||
// make the non-typescript targets put in "data"
|
||||
const tsDeps = def.deps.filter(dep => !!dep.typings);
|
||||
const nodeDeps = def.deps.filter(dep => !dep.typings);
|
||||
args.deps = `[${tsDeps.map(dep => `":${escapeName(dep)}"`).join(', ')}]`;
|
||||
args.data = `[${nodeDeps.map(dep => `":${escapeName(dep)}"`).join(', ')}]`;
|
||||
|
||||
args.declarations = `glob(["${path.join('node_modules', def.name, '**/*.d.ts')}"])`;
|
||||
args.ambient = def.typings.ambient ? 'True' : 'False';
|
||||
args.entry_point = `"${path.join('node_modules', def.name, def.typings.path)}"`;
|
||||
args.root_dir = `"${path.join('node_modules', def.name)}"`;
|
||||
}
|
||||
output += ` ${fn}(${Object.keys(args).map(k => `${k}=${args[k]}`).join(', ')})\n`;
|
||||
}
|
||||
|
||||
let binaries = [];
|
||||
let nodeBinDir = path.join(nodeModules, '.bin');
|
||||
try {
|
||||
binaries = fs.readdirSync(nodeBinDir);
|
||||
} catch (err) {
|
||||
}
|
||||
|
||||
for (const name of binaries) {
|
||||
let link;
|
||||
try {
|
||||
link = fs.readlinkSync(path.join(nodeBinDir, name));
|
||||
} catch (err) {
|
||||
}
|
||||
if (link) {
|
||||
// For each binary found, check if the binary is from a direct
|
||||
// dependency
|
||||
// If so, create a nodejs_binary target for that, pointing to the actual
|
||||
// entrypoint file.
|
||||
const executable = path.join(nodeBinDir, link);
|
||||
const pkg = path.relative(nodeModules, executable).split(path.sep)[0];
|
||||
const executableInPackage = path.relative(path.join(nodeModules, pkg), executable);
|
||||
if (targets.indexOf(pkg) !== -1) {
|
||||
const args = {
|
||||
name: `"${escapeName(name)}_bin"`,
|
||||
srcs: `[":${escapeName(pkg)}"]`,
|
||||
entry_point: `"${path.join('node_modules', pkg, executableInPackage)}"`
|
||||
};
|
||||
output += ` nodejs_binary(${Object.keys(args).map(k => `${k}=${args[k]}`).join(', ')})\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[3]) {
|
||||
if (process.argv[4] === '--verify') {
|
||||
if (fs.readFileSync(process.argv[3]).toString() !== output) {
|
||||
console.error(
|
||||
`Error: ${process.argv[3]} is outdated. Please run:\n\n` +
|
||||
process.argv.slice(0, 4).join(' '));
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
fs.writeFileSync(process.argv[3], output);
|
||||
}
|
||||
} else {
|
||||
process.stdout.write(output);
|
||||
}
|
||||
|
||||
function collectSrcs(target, visited) {
|
||||
const isFirst = !visited;
|
||||
visited = visited || {};
|
||||
return Object
|
||||
.keys(packageMap[target].deps)
|
||||
// If the package is a target,
|
||||
// a) it is a direct dependency: ignore it since it will be in "deps"
|
||||
// b) otherwise: collect its dependencies to "srcs" too
|
||||
.filter(dep => (!isFirst || targets.indexOf(dep) === -1) && !visited.hasOwnProperty(target))
|
||||
.map(dep => collectSrcs(dep, Object.assign({[target]: true}, visited)))
|
||||
.reduce((a, b) => Object.assign(a, b), {[target]: true});
|
||||
}
|
||||
}
|
||||
|
||||
function traceNodeModules(nodeModules) {
|
||||
const packages = [];
|
||||
for (const dir of fs.readdirSync(nodeModules)) {
|
||||
if (dir[0] === '@') {
|
||||
for (const subdir of fs.readdirSync(path.join(nodeModules, dir))) {
|
||||
const pkg = dir + '/' + subdir;
|
||||
if (fs.statSync(path.join(nodeModules, pkg)).isDirectory()) {
|
||||
packages.push(pkg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fs.statSync(path.join(nodeModules, dir)).isDirectory()) {
|
||||
packages.push(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the "external" dependencies given the package directory.
|
||||
* External means that it is from outside of the package directory.
|
||||
*/
|
||||
function tracePackage(packageDir) {
|
||||
let packageJson;
|
||||
try {
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json')));
|
||||
} catch (err) {
|
||||
}
|
||||
|
||||
if (packageJson) {
|
||||
const deps = readDependencies(packageJson);
|
||||
|
||||
const internalNodeModules = path.join(packageDir, 'node_modules');
|
||||
const internalDeps =
|
||||
fs.existsSync(internalNodeModules) ? traceNodeModules(internalNodeModules) : [];
|
||||
|
||||
for (const dep of internalDeps) {
|
||||
Object.assign(deps, tracePackage(path.join(internalNodeModules, dep)).deps);
|
||||
}
|
||||
for (const dep of internalDeps) {
|
||||
delete deps[dep];
|
||||
}
|
||||
|
||||
let typings = null;
|
||||
let typingsPath;
|
||||
|
||||
// Collect d.ts file info if "typings" or "types" is defined
|
||||
if (typeof packageJson.typings === 'string') typingsPath = packageJson.typings;
|
||||
else if (typeof packageJson.types === 'string') typingsPath = packageJson.types;
|
||||
|
||||
if (typingsPath) {
|
||||
if (!typingsPath.match(/\.ts$/)) {
|
||||
typingsPath += '.d.ts';
|
||||
}
|
||||
|
||||
// Apply the heuristic that anything in @types is ambient
|
||||
const looksAmbient = packageDir.indexOf('/@types/') !== -1;
|
||||
|
||||
// Remove the ./ prefix with normalize
|
||||
typings = {path: path.normalize(typingsPath), ambient: looksAmbient};
|
||||
}
|
||||
|
||||
return {deps: deps, typings: typings};
|
||||
} else {
|
||||
// Assume no deps if package.json does not exist / is malformed
|
||||
return {deps: {}, typings: null};
|
||||
}
|
||||
}
|
||||
|
||||
function readDependencies(packageJson, types) {
|
||||
types = types || ['dependencies', 'optionalDependencies', 'peerDependencies'];
|
||||
return types.map(type => packageJson[type]).reduce((a, b) => Object.assign(a, b), {});
|
||||
}
|
||||
|
||||
function escapeName(name) {
|
||||
return name.replace(/@|!/g, '_').replace(/\//g, '_');
|
||||
}
|
||||
|
||||
main();
|
128
build_defs/nodejs.bzl
Normal file
128
build_defs/nodejs.bzl
Normal file
@ -0,0 +1,128 @@
|
||||
load("//build_defs:utils.bzl", "join_paths", "pick_file_in_dir")
|
||||
|
||||
|
||||
_NODEJS_MODULE_ATTRS = {
|
||||
"srcs": attr.label_list(allow_files=True, mandatory=True, cfg=DATA_CFG),
|
||||
"deps": attr.label_list(providers=["nodejs"], cfg=DATA_CFG),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
}
|
||||
_NODEJS_EXECUTABLE_ATTRS = _NODEJS_MODULE_ATTRS + {
|
||||
"entry_point": attr.string(mandatory=True),
|
||||
"_nodejs": attr.label(
|
||||
default = Label("@nodejs//:nodejs"),
|
||||
allow_files = True,
|
||||
executable = True,
|
||||
),
|
||||
"_launcher_template": attr.label(
|
||||
default = Label("//build_defs:nodejs_launcher_template.sh"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
}
|
||||
|
||||
def _nodejs_module_impl(ctx):
|
||||
"""nodejs_module
|
||||
|
||||
Rule for defining a Node.js module.
|
||||
|
||||
Args:
|
||||
srcs: Required. A list of source files that make up the module. The general
|
||||
assumption is that this should be CommonJS files that can run directly on
|
||||
Node.js.
|
||||
deps: A list of Node.js modules that this module depends on.
|
||||
|
||||
Unfortunately, since we do not patch require() currently, the Node.js rules
|
||||
cannot do anything to ensure that modules are resolved at runtime.
|
||||
|
||||
The Node.js wrapper, however, provides these command line arguments:
|
||||
--node_options=--foo_option=bar
|
||||
Passes --foo_option=bar to Node.js as startup options.
|
||||
--node_path=path/to/foo:path/to/bar
|
||||
Adds the specified paths to NODE_PATH after resolving them relative to
|
||||
runfiles.
|
||||
|
||||
You can use them with the "args" kwarg in any Node.js-based target. A
|
||||
convenient option is --node_options=debug, which launches the target in a
|
||||
debugger. Note that however, you have to use `bazel-run.sh` to do that in
|
||||
order to connect stdin.
|
||||
|
||||
Note that Node.js resolves symlinks when loading modules, which is wrong in
|
||||
our bazel environment, since it resolves symlinks that may cross the runfiles
|
||||
boundary. We may be able to use the Node.js flag "--preserve-symlinks"
|
||||
introduced in Node.js 6.2. See https://github.com/nodejs/node/pull/6537
|
||||
"""
|
||||
return struct(
|
||||
files = set(ctx.files.srcs),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs,
|
||||
collect_data = True,
|
||||
collect_default = True,
|
||||
),
|
||||
nodejs = struct(),
|
||||
javascript = struct(
|
||||
files = ctx.files.srcs,
|
||||
),
|
||||
)
|
||||
|
||||
nodejs_module = rule(
|
||||
implementation = _nodejs_module_impl,
|
||||
attrs = _NODEJS_MODULE_ATTRS,
|
||||
)
|
||||
|
||||
|
||||
def _nodejs_binary_impl(ctx):
|
||||
"""nodejs_binary
|
||||
|
||||
Rule for defining a Node.js binary. This creates an executable version of
|
||||
a Node.js module.
|
||||
|
||||
Args:
|
||||
srcs: Required. A list of source files that make up the module.
|
||||
deps: A list of Node.js modules that this module depends on.
|
||||
data: A list of extra files or targets to include in runfiles.
|
||||
entry_point: The main JavaScript file to run. This is resolved relative to
|
||||
the package of the first "srcs" target.
|
||||
"""
|
||||
main_src = ctx.attr.srcs[0]
|
||||
entry_point_file, entry_point_relative_path = pick_file_in_dir(
|
||||
main_src.files, main_src.label, ctx.attr.entry_point)
|
||||
|
||||
ctx.template_action(
|
||||
template = ctx.file._launcher_template,
|
||||
output = ctx.outputs.executable,
|
||||
substitutions = {
|
||||
"{{nodejs}}": ctx.executable._nodejs.short_path,
|
||||
"{{entry_point}}": join_paths(entry_point_file.short_path, entry_point_relative_path),
|
||||
},
|
||||
executable = True,
|
||||
)
|
||||
|
||||
return struct(
|
||||
files = set([ctx.outputs.executable]),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs + ctx.files._nodejs,
|
||||
collect_data = True,
|
||||
collect_default = True,
|
||||
),
|
||||
nodejs = struct(),
|
||||
javascript = struct(
|
||||
files = ctx.files.srcs,
|
||||
),
|
||||
)
|
||||
|
||||
nodejs_binary = rule(
|
||||
implementation = _nodejs_binary_impl,
|
||||
executable = True,
|
||||
attrs = _NODEJS_EXECUTABLE_ATTRS,
|
||||
)
|
||||
|
||||
|
||||
"""nodejs_test
|
||||
|
||||
A variant of nodejs_binary with "test=True".
|
||||
"""
|
||||
nodejs_test = rule(
|
||||
implementation = _nodejs_binary_impl,
|
||||
test = True,
|
||||
attrs = _NODEJS_EXECUTABLE_ATTRS,
|
||||
)
|
42
build_defs/nodejs_launcher_template.sh
Normal file
42
build_defs/nodejs_launcher_template.sh
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z "${RUNFILES}" ]]; then
|
||||
case "${0}" in
|
||||
/*) self="${0}" ;;
|
||||
*) self="${PWD}/${0}" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "${TEST_SRCDIR}" ]]; then
|
||||
export RUNFILES="${TEST_SRCDIR}/angular"
|
||||
elif [[ -d "${self}.runfiles" ]]; then
|
||||
export RUNFILES="${self}.runfiles/angular"
|
||||
else
|
||||
echo "Runfiles directory not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Parse Node.js startup options and our runfiles-relative NODE_PATH option
|
||||
args=()
|
||||
node_options=()
|
||||
runfiles_node_path=()
|
||||
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
--node_options=*)
|
||||
node_options+=( "${arg#--node_options=}" )
|
||||
;;
|
||||
--node_path=*)
|
||||
runfiles_node_path=( $(IFS=":"; echo ${arg#--node_path=}) )
|
||||
;;
|
||||
*)
|
||||
args+=( "${arg}" )
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
export NODE_PATH="$(for P in "${runfiles_node_path[@]}"; do echo -n "${RUNFILES}/${P}:"; done)${NODE_PATH}"
|
||||
|
||||
"${RUNFILES}/{{nodejs}}" "${node_options[@]}" "${RUNFILES}/{{entry_point}}" "${args[@]}"
|
33
build_defs/nodejs_workspace.bzl
Normal file
33
build_defs/nodejs_workspace.bzl
Normal file
@ -0,0 +1,33 @@
|
||||
_ROOT_BUILD_FILE = """\
|
||||
package(default_visibility=["//visibility:public"])
|
||||
|
||||
alias(
|
||||
name = "nodejs",
|
||||
actual = "//:nodejs_bin",
|
||||
)
|
||||
"""
|
||||
|
||||
def _nodejs_workspace_impl(ctx):
|
||||
"""
|
||||
Workspace rule that pulls in the node binary. The binary will be available as
|
||||
//:nodejs in the workspace.
|
||||
|
||||
Args:
|
||||
binary: The name of the node executable in path.
|
||||
"""
|
||||
node = ctx.which(ctx.attr.binary)
|
||||
|
||||
if node == None:
|
||||
fail("Node.js not found in path.")
|
||||
|
||||
ctx.symlink(node, "nodejs_bin")
|
||||
|
||||
# The generated alias is executable because the source file is executable.
|
||||
ctx.file("BUILD", _ROOT_BUILD_FILE, False)
|
||||
|
||||
nodejs_workspace = repository_rule(
|
||||
_nodejs_workspace_impl,
|
||||
attrs = {
|
||||
"binary": attr.string(default="node"),
|
||||
},
|
||||
)
|
118
build_defs/npm_package.bzl
Normal file
118
build_defs/npm_package.bzl
Normal file
@ -0,0 +1,118 @@
|
||||
load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
|
||||
load("//build_defs:utils.bzl", "join_paths", "map_files")
|
||||
|
||||
|
||||
# Remove testing code since we will be using the bundle.
|
||||
# Ideally we would like to break up main and testing into separate compilation
|
||||
# units.
|
||||
def _remove_testing_and_index(ctx, package_dir, files):
|
||||
testing = join_paths(ctx.label.workspace_root, ctx.label.package, package_dir, "testing")
|
||||
index = join_paths(ctx.label.workspace_root, ctx.label.package, package_dir, "index.")
|
||||
return [f for f in files
|
||||
if not f.short_path.startswith(testing) and not f.short_path.startswith(index)]
|
||||
|
||||
def _ts_npm_package_impl(ctx):
|
||||
files = set()
|
||||
|
||||
if len(ctx.attr.srcs) != 1:
|
||||
fail("srcs must be a singleton list", "srcs")
|
||||
|
||||
src = ctx.attr.srcs[0]
|
||||
|
||||
provider = src.typescript.esm if ctx.attr.esm else src.typescript
|
||||
root_dir = provider.package_dir
|
||||
out_dir = join_paths(src.typescript.package_dir, "dist")
|
||||
|
||||
# Assume the input is in bin_dir, except for package.json
|
||||
abs_root_dir = join_paths(ctx.configuration.bin_dir.path, ctx.label.workspace_root,
|
||||
ctx.label.package, root_dir)
|
||||
abs_out_dir = join_paths(ctx.configuration.bin_dir.path, ctx.label.workspace_root,
|
||||
ctx.label.package, out_dir)
|
||||
|
||||
copy_src = provider.files + provider.metadata + provider.source_maps
|
||||
if ctx.attr.esm:
|
||||
copy_src = _remove_testing_and_index(ctx, root_dir, copy_src)
|
||||
copy_dest = map_files(ctx, copy_src, root_dir, out_dir)
|
||||
ctx.action(
|
||||
progress_message = "Copying files for {}".format(ctx.label),
|
||||
inputs = copy_src,
|
||||
outputs = copy_dest,
|
||||
executable = ctx.executable._copy,
|
||||
arguments = ["--rootDir", abs_root_dir, "--outDir", abs_out_dir] + [f.path for f in copy_src],
|
||||
)
|
||||
|
||||
manifest_src = ctx.file.manifest
|
||||
manifest_dest = ctx.new_file(join_paths(out_dir, "package.json"))
|
||||
ctx.action(
|
||||
progress_message = "Copying files for {}".format(ctx.label),
|
||||
inputs = [manifest_src],
|
||||
outputs = [manifest_dest],
|
||||
command = ["cp", manifest_src.path, manifest_dest.path],
|
||||
)
|
||||
|
||||
downlevel_src = provider.declarations
|
||||
if ctx.attr.esm:
|
||||
downlevel_src = _remove_testing_and_index(ctx, root_dir, downlevel_src)
|
||||
downlevel_dest = map_files(ctx, downlevel_src, root_dir, out_dir)
|
||||
ctx.action(
|
||||
progress_message = "Downleveling .d.ts files for {}".format(ctx.label),
|
||||
inputs = downlevel_src,
|
||||
outputs = downlevel_dest,
|
||||
executable = ctx.executable._downlevel_declaration,
|
||||
arguments = ["--node_path=tools", "--rootDir", abs_root_dir, "--outDir", abs_out_dir] +
|
||||
[f.path for f in downlevel_src],
|
||||
)
|
||||
|
||||
# Assume that data is already at the right location
|
||||
files = [manifest_dest] + copy_dest + downlevel_dest + ctx.files.data
|
||||
|
||||
return struct(
|
||||
files = set(files),
|
||||
runfiles = ctx.runfiles(
|
||||
files = list(files),
|
||||
),
|
||||
)
|
||||
|
||||
_ts_npm_package = rule(
|
||||
implementation = _ts_npm_package_impl,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(providers=["typescript"]),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
"manifest": attr.label(allow_files=True, single_file=True, mandatory=True),
|
||||
"module_name": attr.string(),
|
||||
"esm": attr.bool(default=True),
|
||||
|
||||
"_copy": attr.label(default=Label("//build_defs/tools:copy"), executable=True),
|
||||
"_downlevel_declaration": attr.label(
|
||||
default=Label("//build_defs/tools:downlevel_declaration"), executable=True),
|
||||
},
|
||||
)
|
||||
|
||||
def ts_npm_package(*, name, strip_prefix, extension=None, package_dir=None, files=[],
|
||||
mode=None, modes=None, symlinks=None, **kwargs):
|
||||
"""
|
||||
Rule to create an npm package from a ts_library target.
|
||||
|
||||
Args:
|
||||
srcs: The ts_library target.
|
||||
data: Data files to be packaged.
|
||||
manifest: The package.json to be packaged.
|
||||
module_name: The ES module name of the module.
|
||||
strip_prefix: Required. The directory which files in the tarball should be relative to.
|
||||
extension, package_dir, files, mode, modes, symlinks:
|
||||
The corresponding argument in pkg_tar.
|
||||
"""
|
||||
_ts_npm_package(
|
||||
name = name + "_files",
|
||||
**kwargs
|
||||
)
|
||||
pkg_tar(
|
||||
name = name,
|
||||
extension = extension,
|
||||
strip_prefix = strip_prefix,
|
||||
package_dir = package_dir,
|
||||
mode = mode,
|
||||
modes = modes,
|
||||
symlinks = symlinks,
|
||||
files = [":{}_files".format(name)] + files,
|
||||
)
|
64
build_defs/protractor.bzl
Normal file
64
build_defs/protractor.bzl
Normal file
@ -0,0 +1,64 @@
|
||||
def _protractor_test_impl(ctx):
|
||||
"""
|
||||
Rule for running Protractor tests.
|
||||
|
||||
Args:
|
||||
srcs: The targets containing the spec files.
|
||||
deps: JavaScript targets which the tests depend on.
|
||||
data: Data files which the tests depend on. This should include all client-
|
||||
side files required for the tests.
|
||||
config: Required. Protractor config file to use.
|
||||
|
||||
When this rule is run, the runfiles tree will be served at port 8000, rooted
|
||||
at the workspace. Protractor tests can make use of this server. Additionally,
|
||||
you can pass in the argument --serve-only to only run the server, e.g.:
|
||||
|
||||
$ bazel run :foo_e2e_test -- --serve-only
|
||||
"""
|
||||
ctx.template_action(
|
||||
template = ctx.file._launcher_template,
|
||||
output = ctx.outputs.executable,
|
||||
substitutions = {
|
||||
"{{protractor}}": ctx.executable._protractor.short_path,
|
||||
"{{config}}": ctx.file.config.short_path,
|
||||
"{{serve_runfiles}}": ctx.executable._serve_runfiles.short_path,
|
||||
},
|
||||
executable = True,
|
||||
)
|
||||
|
||||
return struct(
|
||||
files = set([ctx.outputs.executable]),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs + ctx.files._protractor + ctx.files._serve_runfiles + ctx.files.config,
|
||||
transitive_files = set(ctx.attr._protractor.default_runfiles.files) + set(ctx.attr._serve_runfiles.default_runfiles.files),
|
||||
collect_data = True,
|
||||
collect_default = True,
|
||||
),
|
||||
)
|
||||
|
||||
_protractor_test = rule(
|
||||
implementation = _protractor_test_impl,
|
||||
executable = True,
|
||||
test = True,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(allow_files=True),
|
||||
"deps": attr.label_list(),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
"config": attr.label(allow_files=True, single_file=True, mandatory=True),
|
||||
|
||||
"_protractor": attr.label(default=Label("//:protractor_bin"), executable=True),
|
||||
"_serve_runfiles": attr.label(
|
||||
default = Label("//build_defs/tools:serve_runfiles"),
|
||||
executable = True,
|
||||
),
|
||||
"_launcher_template": attr.label(
|
||||
default = Label("//build_defs:protractor_launcher_template.sh"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def protractor_test(*, tags=None, **kwargs):
|
||||
tags = tags or []
|
||||
_protractor_test(tags=tags + ["ibazel_notify_changes"], **kwargs)
|
55
build_defs/protractor_launcher_template.sh
Normal file
55
build_defs/protractor_launcher_template.sh
Normal file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z "${RUNFILES}" ]]; then
|
||||
case "${0}" in
|
||||
/*) self="${0}" ;;
|
||||
*) self="${PWD}/${0}" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "${TEST_SRCDIR}" ]]; then
|
||||
export RUNFILES="${TEST_SRCDIR}/angular"
|
||||
elif [[ -d "${self}.runfiles" ]]; then
|
||||
export RUNFILES="${self}.runfiles/angular"
|
||||
else
|
||||
echo "Runfiles directory not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
"${RUNFILES}/{{serve_runfiles}}" &
|
||||
SERVER_PID="$!"
|
||||
|
||||
# Kill the server if we get interrupted.
|
||||
trap "kill ${SERVER_PID}" SIGINT SIGTERM
|
||||
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
--serve-only)
|
||||
SERVE_ONLY=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "${SERVE_ONLY}" ]]; then
|
||||
wait
|
||||
else
|
||||
# Run once.
|
||||
"${RUNFILES}/{{protractor}}" "${RUNFILES}/{{config}}" "$@"
|
||||
|
||||
# Automatically rerun if told to do so
|
||||
if [[ "${IBAZEL_NOTIFY_CHANGES}" == "y" ]]; then
|
||||
while read line; do
|
||||
case "${line}" in
|
||||
"IBAZEL_BUILD_STARTED")
|
||||
;;
|
||||
"IBAZEL_BUILD_COMPLETED SUCCEEDED")
|
||||
"${RUNFILES}/{{protractor}}" "${RUNFILES}/{{config}}" "$@"
|
||||
;;
|
||||
"IBAZEL_BUILD_COMPLETED FAILED")
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
fi
|
45
build_defs/rollup_config_template.js
Normal file
45
build_defs/rollup_config_template.js
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import baseConfig from '{{base_config}}';
|
||||
|
||||
|
||||
const prefixes = [{{prefixes}}];
|
||||
|
||||
/**
|
||||
* A Rollup plugin that (wrongly) assumes that source maps can be found by
|
||||
* suffixing ".map" and tries to do that.
|
||||
*/
|
||||
class LoadSourceMapHeuristicPlugin {
|
||||
load(id) {
|
||||
const code = fs.readFileSync(id).toString();
|
||||
|
||||
let map;
|
||||
try {
|
||||
map = fs.readFileSync(id + '.map').toString();
|
||||
} catch (err) {
|
||||
}
|
||||
|
||||
return {code: code, map: map};
|
||||
}
|
||||
}
|
||||
|
||||
const config = Object.assign({}, baseConfig, {
|
||||
entry: '{{entry}}',
|
||||
dest: '{{dest}}',
|
||||
sourceMap: true,
|
||||
|
||||
banner: '{{banner}}' ? fs.readFileSync('{{banner}}').toString() : '',
|
||||
|
||||
// Make rollup shut up.
|
||||
onwarn: msg => {
|
||||
if (!msg.match(/as external dependency$/)) {
|
||||
console.log(msg);
|
||||
}
|
||||
},
|
||||
|
||||
plugins: (baseConfig.plugins || []).concat([new LoadSourceMapHeuristicPlugin()])
|
||||
});
|
||||
|
||||
export default config;
|
53
build_defs/tests/nodejs/BUILD
Normal file
53
build_defs/tests/nodejs/BUILD
Normal file
@ -0,0 +1,53 @@
|
||||
load("//build_defs:nodejs.bzl", "nodejs_module", "nodejs_test")
|
||||
|
||||
# nodejs_{binary,test} should be runnable.
|
||||
nodejs_test(
|
||||
name = "do_nothing",
|
||||
srcs = ["do_nothing.js"],
|
||||
entry_point = "do_nothing.js",
|
||||
size = "small",
|
||||
)
|
||||
|
||||
# nodejs_{binary,test} should support entrypoint inside a directory file.
|
||||
nodejs_test(
|
||||
name = "inside_directory",
|
||||
srcs = ["dir"],
|
||||
entry_point = "dir/index.js",
|
||||
size = "small",
|
||||
)
|
||||
|
||||
# nodejs_{binary,test} should support node_options.
|
||||
nodejs_test(
|
||||
name = "node_options",
|
||||
srcs = ["node_options.js"],
|
||||
entry_point = "node_options.js",
|
||||
args = ["--node_options=--expose-gc"],
|
||||
size = "small",
|
||||
)
|
||||
|
||||
# nodejs_module should be accessible from downstream dependencies.
|
||||
nodejs_module(
|
||||
name = "foo-module",
|
||||
srcs = ["foo-module/index.js"],
|
||||
)
|
||||
|
||||
nodejs_test(
|
||||
name = "use_relative",
|
||||
srcs = ["use_relative.js"],
|
||||
entry_point = "use_relative.js",
|
||||
size = "small",
|
||||
)
|
||||
|
||||
# nodejs_{binary,test} should support node_path.
|
||||
nodejs_test(
|
||||
name = "use_absolute",
|
||||
srcs = ["use_absolute.js"],
|
||||
deps = ["foo-module"],
|
||||
entry_point = "use_absolute.js",
|
||||
args = ["--node_path=build_defs/tests/nodejs"],
|
||||
size = "small",
|
||||
)
|
||||
|
||||
test_suite(
|
||||
name = "all_tests",
|
||||
)
|
3
build_defs/tests/nodejs/foo-module/index.js
Normal file
3
build_defs/tests/nodejs/foo-module/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = 'foo';
|
5
build_defs/tests/nodejs/node_options.js
Normal file
5
build_defs/tests/nodejs/node_options.js
Normal file
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
assert.equal(typeof global.gc, 'function');
|
7
build_defs/tests/nodejs/use_absolute.js
Normal file
7
build_defs/tests/nodejs/use_absolute.js
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const foo = require('foo-module');
|
||||
|
||||
assert.equal(foo, 'foo');
|
7
build_defs/tests/nodejs/use_relative.js
Normal file
7
build_defs/tests/nodejs/use_relative.js
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const foo = require('./foo-module');
|
||||
|
||||
assert.equal(foo, 'foo');
|
168
build_defs/tests/typescript/BUILD
Normal file
168
build_defs/tests/typescript/BUILD
Normal file
@ -0,0 +1,168 @@
|
||||
load("//build_defs:typescript.bzl", "ts_library", "ts_ext_library")
|
||||
load("//build_defs:nodejs.bzl", "nodejs_test")
|
||||
|
||||
# ts_library should reject empty srcs. (FAIL)
|
||||
ts_library(
|
||||
name = "nothing_FAIL",
|
||||
srcs = glob(["blackhole/**/*.ts"]),
|
||||
tsconfig = "tsconfig.json",
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
# ts_library should build a module at a normal position.
|
||||
ts_library(
|
||||
name = "basic",
|
||||
srcs = glob(["*.ts"]),
|
||||
tsconfig = "tsconfig.json",
|
||||
)
|
||||
|
||||
# ts_library should default root_dir to tsconfig.json location.
|
||||
# ts_library should support aliasing modules.
|
||||
ts_library(
|
||||
name = "nested",
|
||||
srcs = ["nested/@nested/index.ts"],
|
||||
# Interestingly, paths starting with "@" will be recognized as a special
|
||||
# label, so we cannot address "@nested/tsconfig.json" directly.
|
||||
tsconfig = "nested/@nested/tsconfig.json",
|
||||
module_name = "@nested/nested",
|
||||
)
|
||||
|
||||
# ts_library should support importing deps modules.
|
||||
# ts_library should support importing sub-modules.
|
||||
ts_library(
|
||||
name = "importing_modules",
|
||||
srcs = ["importing_modules/index.ts"],
|
||||
deps = [":basic", ":nested"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "importing_modules",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "internal_stuff",
|
||||
srcs = ["internal_stuff/index.ts"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "internal_stuff",
|
||||
)
|
||||
|
||||
# ts_library should not use @internal declarations by default. (FAIL)
|
||||
ts_library(
|
||||
name = "use_internal_wrong_FAIL",
|
||||
srcs = ["use_internal/index.ts"],
|
||||
deps = [":internal_stuff"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_internal",
|
||||
out_dir = "use_internal_wrong",
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
# ts_library should check that deps_use_internal is a subset of deps. (FAIL)
|
||||
ts_library(
|
||||
name = "use_internal_subset_FAIL",
|
||||
srcs = ["use_internal/index.ts"],
|
||||
deps_use_internal = [":internal_stuff"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_internal",
|
||||
out_dir = "use_internal_wrong",
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
# ts_library should support using @internal declarations.
|
||||
ts_library(
|
||||
name = "use_internal",
|
||||
srcs = ["use_internal/index.ts"],
|
||||
deps = [":internal_stuff"],
|
||||
deps_use_internal = [":internal_stuff"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_internal",
|
||||
)
|
||||
|
||||
# ts_library should make transitive declarations available.
|
||||
ts_library(
|
||||
name = "use_transitive",
|
||||
srcs = ["use_transitive/index.ts"],
|
||||
deps = [":use_internal"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_transitive",
|
||||
)
|
||||
|
||||
# ts_library should load ambient declarations from ts_ext_library.
|
||||
ts_ext_library(
|
||||
name = "ambient",
|
||||
declarations = glob(["ambient/*.d.ts"]),
|
||||
root_dir = "ambient",
|
||||
ambient = True,
|
||||
entry_point = "ambient/index.d.ts",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "use_ambient",
|
||||
srcs = ["use_ambient/index.ts"],
|
||||
deps = [":ambient"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_ambient",
|
||||
)
|
||||
|
||||
# ts_library should load non-ambient declarations from ts_ext_library.
|
||||
ts_ext_library(
|
||||
name = "non_ambient",
|
||||
declarations = glob(["non_ambient/*.d.ts"]),
|
||||
root_dir = "non_ambient",
|
||||
ambient = False,
|
||||
entry_point = "non_ambient/index.d.ts",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "use_non_ambient",
|
||||
srcs = ["use_non_ambient/index.ts"],
|
||||
deps = [":non_ambient"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_non_ambient",
|
||||
)
|
||||
|
||||
# ts_library should make transitive ambient declarations available.
|
||||
ts_library(
|
||||
name = "use_transitive_ambient",
|
||||
srcs = ["use_transitive_ambient/index.ts"],
|
||||
deps = [":use_ambient"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_transitive_ambient",
|
||||
)
|
||||
|
||||
# ts_library should support using only generated sources.
|
||||
genrule(
|
||||
name = "generated_source",
|
||||
outs = ["use_generated_source/index.ts"],
|
||||
cmd = "echo \"export const useGeneratedSource = 'useGeneratedSource';\" > $@",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "use_generated_source",
|
||||
srcs = [":generated_source"],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "use_generated_source",
|
||||
)
|
||||
|
||||
# ts_library should be usable in nodejs_binary.
|
||||
ts_library(
|
||||
name = "assert",
|
||||
srcs = ["assert/index.ts"],
|
||||
deps = [
|
||||
# Do not test basic and nested since they are too troublesome to set up
|
||||
# NODE_PATH for.
|
||||
":use_internal",
|
||||
":use_transitive",
|
||||
":use_ambient",
|
||||
":use_transitive_ambient",
|
||||
":use_generated_source",
|
||||
],
|
||||
tsconfig = "tsconfig.json",
|
||||
root_dir = "assert",
|
||||
)
|
||||
|
||||
nodejs_test(
|
||||
name = "assert_test",
|
||||
srcs = [":assert"],
|
||||
entry_point = "assert/index.js",
|
||||
size = "small",
|
||||
args = ["--node_path=build_defs/tests/typescript"],
|
||||
)
|
3
build_defs/tests/typescript/ambient/index.d.ts
vendored
Normal file
3
build_defs/tests/typescript/ambient/index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import './process';
|
||||
|
||||
declare const process: any;
|
1
build_defs/tests/typescript/ambient/process.d.ts
vendored
Normal file
1
build_defs/tests/typescript/ambient/process.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare const process: any;
|
15
build_defs/tests/typescript/assert/index.ts
Normal file
15
build_defs/tests/typescript/assert/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {useInternal} from 'use_internal';
|
||||
import {useTransitive} from 'use_transitive';
|
||||
import {useAmbient} from 'use_ambient';
|
||||
import {useTransitiveAmbient} from 'use_transitive_ambient';
|
||||
import {useGeneratedSource} from 'use_generated_source';
|
||||
|
||||
// We cannot use @types/node since we already declared process: any.
|
||||
declare const require: any;
|
||||
const assert: any = require('assert');
|
||||
|
||||
assert.equal(useInternal, 'new A().b');
|
||||
assert.equal(useTransitive, 'new A().c');
|
||||
assert.equal(useAmbient, process.cwd());
|
||||
assert.equal(useTransitiveAmbient, process.cwd());
|
||||
assert.equal(useGeneratedSource, 'useGeneratedSource');
|
11
build_defs/tests/typescript/importing_modules/index.ts
Normal file
11
build_defs/tests/typescript/importing_modules/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// import module at package root
|
||||
import {basic} from 'basic';
|
||||
|
||||
// import module in a subdir from an alias
|
||||
import {nested} from '@nested/nested';
|
||||
|
||||
// import file from module
|
||||
import {sub} from 'basic/sub';
|
||||
|
||||
|
||||
export const importingModules = basic + nested + sub;
|
1
build_defs/tests/typescript/index.ts
Normal file
1
build_defs/tests/typescript/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export const basic = 'basic';
|
4
build_defs/tests/typescript/internal_stuff/index.ts
Normal file
4
build_defs/tests/typescript/internal_stuff/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export class A {
|
||||
/** @internal */ b = 'new A().b';
|
||||
c = 'new A().c';
|
||||
}
|
1
build_defs/tests/typescript/nested/@nested/index.ts
Normal file
1
build_defs/tests/typescript/nested/@nested/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export const nested = 'nested';
|
8
build_defs/tests/typescript/nested/@nested/tsconfig.json
Normal file
8
build_defs/tests/typescript/nested/@nested/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
1
build_defs/tests/typescript/non_ambient/foo.d.ts
vendored
Normal file
1
build_defs/tests/typescript/non_ambient/foo.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export declare const foo: string;
|
1
build_defs/tests/typescript/non_ambient/index.d.ts
vendored
Normal file
1
build_defs/tests/typescript/non_ambient/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export * from './foo';
|
1
build_defs/tests/typescript/sub.ts
Normal file
1
build_defs/tests/typescript/sub.ts
Normal file
@ -0,0 +1 @@
|
||||
export const sub = 'sub';
|
8
build_defs/tests/typescript/tsconfig.json
Normal file
8
build_defs/tests/typescript/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
1
build_defs/tests/typescript/use_ambient/index.ts
Normal file
1
build_defs/tests/typescript/use_ambient/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export const useAmbient = process.cwd();
|
6
build_defs/tests/typescript/use_internal/index.ts
Normal file
6
build_defs/tests/typescript/use_internal/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {A} from 'internal_stuff';
|
||||
|
||||
export class B extends A {
|
||||
}
|
||||
|
||||
export const useInternal = new A().b;
|
1
build_defs/tests/typescript/use_non_ambient/index.ts
Normal file
1
build_defs/tests/typescript/use_non_ambient/index.ts
Normal file
@ -0,0 +1 @@
|
||||
import {foo} from 'non_ambient';
|
4
build_defs/tests/typescript/use_transitive/index.ts
Normal file
4
build_defs/tests/typescript/use_transitive/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import {B} from 'use_internal';
|
||||
|
||||
// c is an inherited property from A
|
||||
export const useTransitive = new B().c;
|
@ -0,0 +1 @@
|
||||
export const useTransitiveAmbient = process.cwd();
|
52
build_defs/tools/BUILD
Normal file
52
build_defs/tools/BUILD
Normal file
@ -0,0 +1,52 @@
|
||||
package(default_visibility=["//visibility:public"])
|
||||
|
||||
load("//build_defs:nodejs.bzl", "nodejs_binary")
|
||||
|
||||
nodejs_binary(
|
||||
name = "serve_runfiles",
|
||||
srcs = ["serve_runfiles.js"],
|
||||
deps = [
|
||||
"//:gulp-connect",
|
||||
"//:cors",
|
||||
],
|
||||
entry_point = "serve_runfiles.js",
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "merge_tsconfig",
|
||||
srcs = ["merge_tsconfig.js"],
|
||||
deps = ["//:typescript"],
|
||||
entry_point = "merge_tsconfig.js",
|
||||
)
|
||||
|
||||
sh_binary(
|
||||
name = "uglifyjs_wrapped",
|
||||
srcs = ["uglifyjs_wrapped.sh"],
|
||||
data = ["//:uglifyjs_bin"],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "flatten_sourcemap",
|
||||
srcs = ["flatten_sourcemap.js"],
|
||||
data = ["//:source-map", "//:tsc_bin"],
|
||||
entry_point = "flatten_sourcemap.js",
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "copy",
|
||||
srcs = ["copy.js"],
|
||||
deps = [
|
||||
"//:minimist",
|
||||
],
|
||||
entry_point = "copy.js",
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "downlevel_declaration",
|
||||
srcs = ["downlevel_declaration.js"],
|
||||
deps = [
|
||||
"//:minimist",
|
||||
"//:tsc-wrapped",
|
||||
],
|
||||
entry_point = "downlevel_declaration.js",
|
||||
)
|
23
build_defs/tools/copy.js
Normal file
23
build_defs/tools/copy.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const minimist = require('minimist');
|
||||
const path = require('path');
|
||||
|
||||
function main(argv) {
|
||||
const args = minimist(argv);
|
||||
const rootDir = path.resolve(args.rootDir);
|
||||
const outDir = path.resolve(args.outDir);
|
||||
|
||||
for (const fileName of args._) {
|
||||
const input = path.resolve(fileName);
|
||||
if (input.substr(0, rootDir.length) !== rootDir) {
|
||||
throw new Error(`${input} not in ${rootDir}`);
|
||||
}
|
||||
const output = path.join(outDir, input.substr(rootDir.length));
|
||||
// Simply let error propagate and crash the process
|
||||
fs.createReadStream(input).pipe(fs.createWriteStream(output));
|
||||
}
|
||||
}
|
||||
|
||||
main(process.argv.slice(2));
|
30
build_defs/tools/downlevel_declaration.js
Normal file
30
build_defs/tools/downlevel_declaration.js
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const minimist = require('minimist');
|
||||
const path = require('path');
|
||||
const ts = require('typescript');
|
||||
|
||||
const downlevelDeclaration =
|
||||
require('@angular/tsc-wrapped/src/dts_downleveler').downlevelDeclaration;
|
||||
|
||||
function main(argv) {
|
||||
const args = minimist(argv);
|
||||
const rootDir = path.resolve(args.rootDir);
|
||||
const outDir = path.resolve(args.outDir);
|
||||
|
||||
for (const fileName of args._) {
|
||||
const input = path.resolve(fileName);
|
||||
if (input.substr(0, rootDir.length) !== rootDir) {
|
||||
throw new Error(`${input} not in ${rootDir}`);
|
||||
}
|
||||
const output = path.join(outDir, input.substr(rootDir.length));
|
||||
|
||||
const inContent = fs.readFileSync(input).toString();
|
||||
const outContent = downlevelDeclaration(input, inContent, ts.ScriptTarget.ES2015);
|
||||
|
||||
fs.writeFileSync(output, outContent);
|
||||
}
|
||||
}
|
||||
|
||||
main(process.argv.slice(2));
|
108
build_defs/tools/flatten_sourcemap.js
Normal file
108
build_defs/tools/flatten_sourcemap.js
Normal file
@ -0,0 +1,108 @@
|
||||
'use strict';
|
||||
|
||||
const childProcess = require('child_process');
|
||||
const fs = require('fs');
|
||||
const sourceMap = require('source-map'), SourceMapConsumer = sourceMap.SourceMapConsumer,
|
||||
SourceMapGenerator = sourceMap.SourceMapGenerator;
|
||||
const path = require('path');
|
||||
|
||||
const SOURCE_MAPPING_URL_REGEXP = /^\/\/# sourceMappingURL=(.*)$/;
|
||||
|
||||
function printUsageAndExit() {
|
||||
console.error(`Usage: ${process.argv[0]} ${process.argv[1]} <input .js file> [-- <command...>]
|
||||
|
||||
Takes an input JavaScript file that has at the end of the file a list of
|
||||
//# sourceMappingURL=<source map location>
|
||||
tokens overwrites it and its source map with a flattened source map.
|
||||
Optionally runs the specified command before doing so.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function main(fileName) {
|
||||
const lines = fs.readFileSync(fileName).toString().split('\n');
|
||||
if (!lines[lines.length - 1]) {
|
||||
// Remove Last EOL
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
const sourceMaps = [];
|
||||
let i;
|
||||
for (i = lines.length - 1; i >= 0; i--) {
|
||||
const match = lines[i].match(SOURCE_MAPPING_URL_REGEXP);
|
||||
if (match) {
|
||||
sourceMaps.unshift(match[1].trim());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const contents = lines.slice(0, i + 1);
|
||||
|
||||
const maps = sourceMaps.map(m => {
|
||||
const mapName = path.join(path.dirname(fileName), m);
|
||||
const sourceMap = JSON.parse(fs.readFileSync(mapName).toString());
|
||||
return {fileName: mapName, sourceMap: sourceMap, consumer: new SourceMapConsumer(sourceMap)};
|
||||
});
|
||||
|
||||
const firstMap = maps.shift();
|
||||
|
||||
// Create an empty source map with the inline sources that we want to keep
|
||||
const generator = SourceMapGenerator.fromSourceMap(new SourceMapConsumer({
|
||||
version: 3,
|
||||
file: path.basename(fileName),
|
||||
sourceRoot: '',
|
||||
sources: firstMap.sourceMap.sources,
|
||||
sourcesContent: firstMap.sourceMap.sourcesContent,
|
||||
mappings: ''
|
||||
}));
|
||||
|
||||
for (const map of maps) {
|
||||
if (map.sourceMap.sources.length !== 1) {
|
||||
throw new Error(
|
||||
`${map.fileName} has more than one source. Subsequent source maps must only be 1-to-1 mappings!`);
|
||||
}
|
||||
}
|
||||
|
||||
firstMap.consumer.eachMapping(firstMapping => {
|
||||
const original = {line: firstMapping.originalLine, column: firstMapping.originalColumn};
|
||||
|
||||
let finals = [{line: firstMapping.generatedLine, column: firstMapping.generatedColumn}];
|
||||
for (const map of maps) {
|
||||
finals = finals
|
||||
.map(
|
||||
loc => map.consumer.generatedPositionFor(
|
||||
{source: map.sourceMap.sources[0], line: loc.line, column: loc.column}))
|
||||
.filter(loc => loc.line !== null && loc.column !== null)
|
||||
}
|
||||
|
||||
for (const loc of finals) {
|
||||
generator.addMapping({
|
||||
source: firstMapping.source,
|
||||
original: original,
|
||||
generated: loc,
|
||||
name: firstMapping.name
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
fs.writeFileSync(maps[maps.length - 1].fileName, generator.toString());
|
||||
// Match the TypeScript behavior that no EOL is emitted
|
||||
fs.writeFileSync(fileName, contents.join('\n'));
|
||||
}
|
||||
|
||||
if (process.argv.length >= 3) {
|
||||
if (process.argv.length > 3) {
|
||||
const cmdArgs = process.argv.slice(3);
|
||||
if (cmdArgs[0] === '--') {
|
||||
cmdArgs.shift();
|
||||
}
|
||||
const result =
|
||||
childProcess.spawnSync(process.argv[4], process.argv.slice(5), {stdio: 'inherit'});
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status);
|
||||
}
|
||||
}
|
||||
main(process.argv[2]);
|
||||
|
||||
} else {
|
||||
printUsageAndExit();
|
||||
}
|
104
build_defs/tools/merge_tsconfig.js
Normal file
104
build_defs/tools/merge_tsconfig.js
Normal file
@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const ts = require('typescript');
|
||||
|
||||
function main(argv) {
|
||||
if (!argv.length) {
|
||||
console.error(`Usage: ${process.argv[0]} ${process.argv[1]}
|
||||
[--out <out file>] [<--file <file>|<json string>>...]
|
||||
|
||||
Merges multiple portions of tsconfig.json in a way that the output tsconfig.json
|
||||
is semantically valid.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const tsconfig = {};
|
||||
let out = null;
|
||||
|
||||
while (argv.length) {
|
||||
const arg = argv.shift();
|
||||
if (arg === '--file') {
|
||||
const file = argv.shift();
|
||||
mergeTsconfig(tsconfig, parse(file, fs.readFileSync(file).toString()));
|
||||
} else if (arg === '--out') {
|
||||
out = argv.shift();
|
||||
} else {
|
||||
mergeTsconfig(tsconfig, parse(/* fileName */ '<input argument>', arg));
|
||||
}
|
||||
}
|
||||
|
||||
normalizeTsconfig(tsconfig);
|
||||
|
||||
if (out) {
|
||||
fs.writeFileSync(out, JSON.stringify(tsconfig, null, 2));
|
||||
} else {
|
||||
console.log(JSON.stringify(tsconfig, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
function mergeTsconfig(merged, next) {
|
||||
// File lists are not merged but replaced.
|
||||
if ((merged.files || merged.include || merged.exclude) &&
|
||||
(next.files || next.include || next.exclude)) {
|
||||
delete merged.files;
|
||||
delete merged.include;
|
||||
delete merged.exclude;
|
||||
}
|
||||
if (merged.compilerOptions && next.compilerOptions) {
|
||||
// Path mappings are not merged but replaced.
|
||||
if (merged.compilerOptions.paths && next.compilerOptions.paths) {
|
||||
delete merged.compilerOptions.paths;
|
||||
}
|
||||
}
|
||||
deepMerge(merged, next);
|
||||
}
|
||||
|
||||
function normalizeTsconfig(tsconfig) {
|
||||
const compilerOptions = tsconfig.compilerOptions;
|
||||
if (!compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) {
|
||||
// These options require sourceMap or inlineSourceMap to be set.
|
||||
delete compilerOptions.inlineSources;
|
||||
delete compilerOptions.mapRoot;
|
||||
delete compilerOptions.sourceRoot;
|
||||
}
|
||||
}
|
||||
|
||||
function deepMerge(x, y) {
|
||||
if (Array.isArray(x) && Array.isArray(y)) {
|
||||
for (const i of y) {
|
||||
if (x.indexOf(i) === -1) {
|
||||
x.push(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const key in x) {
|
||||
if (key in y) {
|
||||
if (typeof x[key] === 'object' && typeof y[key] === 'object') {
|
||||
deepMerge(x[key], y[key]);
|
||||
} else {
|
||||
x[key] = y[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in y) {
|
||||
if (!(key in x)) {
|
||||
x[key] = y[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parse(fileName, jsonText) {
|
||||
// This parses a JSON-ish file with comments.
|
||||
const result = ts.parseConfigFileTextToJson(fileName, jsonText);
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(result.error.messageText);
|
||||
}
|
||||
|
||||
return result.config;
|
||||
}
|
||||
|
||||
main(process.argv.slice(2));
|
14
build_defs/tools/serve_runfiles.js
Normal file
14
build_defs/tools/serve_runfiles.js
Normal file
@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const connect = require('gulp-connect');
|
||||
const cors = require('cors');
|
||||
|
||||
console.log(`Serving ${process.env.RUNFILES}`);
|
||||
|
||||
connect.server({
|
||||
root: `${process.env.RUNFILES}`,
|
||||
port: 8000,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()],
|
||||
});
|
37
build_defs/tools/uglifyjs_wrapped.sh
Executable file
37
build_defs/tools/uglifyjs_wrapped.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This wrapper adds --preamble-file option to uglifyjs.
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z "${RUNFILES}" ]]; then
|
||||
case "${0}" in
|
||||
/*) self="${0}" ;;
|
||||
*) self="${PWD}/${0}" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "${TEST_SRCDIR}" ]]; then
|
||||
export RUNFILES="${TEST_SRCDIR}/angular"
|
||||
elif [[ -d "${self}.runfiles" ]]; then
|
||||
export RUNFILES="${self}.runfiles/angular"
|
||||
else
|
||||
echo "Runfiles directory not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
ARGS=()
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--preamble-file)
|
||||
shift
|
||||
ARGS+=( "--preamble" "$(cat "${1}")" )
|
||||
;;
|
||||
*)
|
||||
ARGS+=( "${1}" )
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
"${RUNFILES}/uglifyjs_bin" "${ARGS[@]}"
|
114
build_defs/ts_api_guardian.bzl
Normal file
114
build_defs/ts_api_guardian.bzl
Normal file
@ -0,0 +1,114 @@
|
||||
load("//build_defs:utils.bzl", "pick_file")
|
||||
|
||||
|
||||
def _public_api_impl(ctx):
|
||||
"""
|
||||
Rule for generating golden files with ts-api-guardian.
|
||||
|
||||
Args:
|
||||
srcs: The ts_library targets containing the ts files.
|
||||
entry_points: The paths for entrypoint files.
|
||||
"""
|
||||
entry_points = []
|
||||
declaration_files = []
|
||||
|
||||
for src in ctx.attr.srcs:
|
||||
declaration_files += src.typescript.declarations
|
||||
|
||||
entry_points = [pick_file(declaration_files, ctx.label, ep) for ep in ctx.attr.entry_points]
|
||||
|
||||
ctx.template_action(
|
||||
template = ctx.file._launcher_template,
|
||||
output = ctx.outputs.executable,
|
||||
substitutions = {
|
||||
"{{ts_api_guardian}}": ctx.executable._ts_api_guardian.short_path,
|
||||
"{{mode}}": "out",
|
||||
"{{root_dir}}": ctx.attr.root_dir,
|
||||
"{{golden_dir}}": ctx.attr.out_dir,
|
||||
"{{arguments}}": " ".join(ctx.attr.arguments),
|
||||
"{{entry_points}}": " ".join([f.short_path for f in entry_points]),
|
||||
},
|
||||
executable = True,
|
||||
)
|
||||
|
||||
return struct(
|
||||
files = set([ctx.outputs.executable]),
|
||||
runfiles = ctx.runfiles(
|
||||
files = declaration_files + ctx.files._ts_api_guardian,
|
||||
transitive_files = set(ctx.attr._ts_api_guardian.default_runfiles.files),
|
||||
),
|
||||
public_api = struct(
|
||||
srcs = declaration_files,
|
||||
root_dir = ctx.attr.root_dir,
|
||||
golden_dir = ctx.attr.out_dir,
|
||||
arguments = " ".join(ctx.attr.arguments),
|
||||
entry_points = entry_points,
|
||||
),
|
||||
)
|
||||
|
||||
public_api = rule(
|
||||
implementation = _public_api_impl,
|
||||
executable = True,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(allow_files=True),
|
||||
"entry_points": attr.string_list(mandatory=True),
|
||||
"root_dir": attr.string(default="."),
|
||||
"out_dir": attr.string(mandatory=True),
|
||||
"arguments": attr.string_list(),
|
||||
|
||||
"_ts_api_guardian": attr.label(default=Label("//:ts-api-guardian_bin"), executable=True),
|
||||
"_launcher_template": attr.label(
|
||||
default = Label("//build_defs:ts_api_guardian_launcher_template.sh"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _public_api_test_impl(ctx):
|
||||
"""
|
||||
Rule for running ts-api-guardian test.
|
||||
|
||||
Args:
|
||||
public_api: The corresponding public_api target.
|
||||
"""
|
||||
public_api = ctx.attr.public_api.public_api
|
||||
ctx.template_action(
|
||||
template = ctx.file._launcher_template,
|
||||
output = ctx.outputs.executable,
|
||||
substitutions = {
|
||||
"{{ts_api_guardian}}": ctx.executable._ts_api_guardian.short_path,
|
||||
"{{mode}}": "verify",
|
||||
"{{root_dir}}": public_api.root_dir,
|
||||
"{{golden_dir}}": public_api.golden_dir,
|
||||
"{{arguments}}": public_api.arguments,
|
||||
"{{entry_points}}": " ".join([f.short_path for f in public_api.entry_points]),
|
||||
},
|
||||
executable = True,
|
||||
)
|
||||
|
||||
return struct(
|
||||
files = set([ctx.outputs.executable]),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs + public_api.srcs + ctx.files._ts_api_guardian + ctx.files.public_api,
|
||||
transitive_files = set(ctx.attr._ts_api_guardian.default_runfiles.files),
|
||||
),
|
||||
)
|
||||
|
||||
public_api_test = rule(
|
||||
implementation = _public_api_test_impl,
|
||||
executable = True,
|
||||
test = True,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(allow_files=True),
|
||||
"public_api": attr.label(providers=["public_api"], mandatory=True),
|
||||
|
||||
"_ts_api_guardian": attr.label(default=Label("//:ts-api-guardian_bin"), executable=True),
|
||||
"_launcher_template": attr.label(
|
||||
default = Label("//build_defs:ts_api_guardian_launcher_template.sh"),
|
||||
allow_files = True,
|
||||
single_file = True,
|
||||
),
|
||||
},
|
||||
)
|
44
build_defs/ts_api_guardian_launcher_template.sh
Normal file
44
build_defs/ts_api_guardian_launcher_template.sh
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z "${RUNFILES}" ]]; then
|
||||
case "${0}" in
|
||||
/*) self="${0}" ;;
|
||||
*) self="${PWD}/${0}" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "${TEST_SRCDIR}" ]]; then
|
||||
export RUNFILES="${TEST_SRCDIR}/angular"
|
||||
elif [[ -d "${self}.runfiles" ]]; then
|
||||
export RUNFILES="${self}.runfiles/angular"
|
||||
else
|
||||
echo "Runfiles directory not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "{{mode}}" == "out" ]]; then
|
||||
# For out, we need to write to execroot
|
||||
# We use a WORKSPACE file to determine workspace root
|
||||
WORKSPACE="${0}"
|
||||
|
||||
while [[ ${#WORKSPACE} > 1 ]] && ! [[ -h "${WORKSPACE}/WORKSPACE" ]]; do
|
||||
WORKSPACE="$(dirname "${WORKSPACE}")"
|
||||
done
|
||||
if [[ -z "${WORKSPACE}" ]] || [[ "${WORKSPACE}" == .* ]]; then
|
||||
echo "Cannot locate execroot." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GOLDEN_DIR="${WORKSPACE}/{{golden_dir}}"
|
||||
else
|
||||
# For verify, we can just use the copy in runfiles
|
||||
GOLDEN_DIR="{{golden_dir}}"
|
||||
fi
|
||||
|
||||
# entry_points are relative to RUNFILES, so we have to cd to there
|
||||
cd ${RUNFILES}
|
||||
|
||||
"./{{ts_api_guardian}}" \
|
||||
--rootDir {{root_dir}} --{{mode}}Dir "${GOLDEN_DIR}" --color {{arguments}} {{entry_points}}
|
439
build_defs/typescript.bzl
Normal file
439
build_defs/typescript.bzl
Normal file
@ -0,0 +1,439 @@
|
||||
load("//build_defs:utils.bzl", "join_paths", "normalize_path", "map_files", "drop_dir",
|
||||
"pseudo_json_encode", "pick_file_in_dir", "pick_provider")
|
||||
|
||||
|
||||
def _ts_library_impl(ctx):
|
||||
"""ts_library
|
||||
|
||||
Rule to compile TypeScript code.
|
||||
|
||||
Args:
|
||||
deps: ts_* dependencies.
|
||||
deps_use_internal: ts_library dependencies for which @internal declarations
|
||||
should be used. Must be a subset of deps. Note that if this target is used
|
||||
downstream, you should ensure that the resultant declarations will be
|
||||
compatible with upstream declarations when all @internal's are stripped.
|
||||
module_name: The module name of the package. Defaults to the target name.
|
||||
root_dir: The TypeScript rootDir relative to the package. Defaults to the
|
||||
location of the tsconfig.json file.
|
||||
out_dir: The TypeScript outDir relative to the package. Defaults to
|
||||
root_dir.
|
||||
source_map: Corresponds to sourceMap in TypeScript.
|
||||
inline_source_map: Corresponds to inlineSourceMap in TypeScript.
|
||||
is_leaf: Declares that this ts_library will not be depended on by other
|
||||
TypeScript libraries. This disables declaration and metadata generation.
|
||||
"""
|
||||
# Directory structure:
|
||||
# bazel-angular/ (execroot)
|
||||
# bazel-out/foo/bin/
|
||||
# path-to-package/
|
||||
# out/dir/
|
||||
# *.js, *.d.ts, *.metadata.json
|
||||
# esm/*.js
|
||||
# internal/*.d.ts
|
||||
# target-label_flavor_tsconfig.json
|
||||
# path-to-package/
|
||||
# *.ts
|
||||
# *.d.ts
|
||||
# tsconfig.json
|
||||
|
||||
# Merged tree: (e.g. in runfiles)
|
||||
# path-to-package/
|
||||
# *.ts
|
||||
# out/dir/ (if any)
|
||||
# *.js, *.d.ts, *.metadata.json
|
||||
# esm/*.js, *.d.ts, *.metadata.json
|
||||
# internal/*.d.ts, *.metadata.json
|
||||
|
||||
for src in ctx.attr.srcs:
|
||||
if src.label.package != ctx.label.package:
|
||||
# Sources can be in sub-folders, but not in sub-packages.
|
||||
fail("Sources must be in the same package as the ts_library rule, " +
|
||||
"but {} is not in {}".format(src.label, ctx.label.package), "srcs")
|
||||
|
||||
for dep in ctx.attr.deps:
|
||||
if dep.typescript.is_leaf:
|
||||
fail("{} is a leaf library and cannot be depended on".format(dep.label), "deps")
|
||||
|
||||
for dep in ctx.attr.deps_use_internal:
|
||||
if dep not in ctx.attr.deps:
|
||||
fail("deps_use_internal must be a subset of deps", "deps_use_internal")
|
||||
if not hasattr(dep.typescript, 'internal'):
|
||||
fail("dep {} does not have @internal declarations.".format(dep.label),
|
||||
"deps_use_internal")
|
||||
|
||||
if not ctx.files.srcs:
|
||||
fail("No source file found", "srcs")
|
||||
|
||||
# Find out the correct rootDir to use. This allows using generated files as
|
||||
# input, which is needed e.g. for compiler codegen test.
|
||||
# Note that despite the name, TypeScript does not use rootDirs to compute the
|
||||
# output path.
|
||||
source_roots = list(set([src.root.path for src in ctx.files.srcs]))
|
||||
if len(source_roots) > 1:
|
||||
fail("Mixing source and generated files as input is not supported.", "srcs")
|
||||
|
||||
# There are four types of input files:
|
||||
# - *.ts files in srcs: load with "files"
|
||||
# - *.d.ts files in srcs: load with "files" (We need this for convenience in
|
||||
# e.g. playground e2e tests)
|
||||
# - Non-ambient *.d.ts files: load with "compilerOptions"."paths" (tc_paths)
|
||||
# - Module/bunbled *.d.ts files: load main file with "files" (tc_types)
|
||||
#
|
||||
# Regardless of the type, all of them have to be specified as inputs to the
|
||||
# bazel action.
|
||||
|
||||
# These values are propagated transitively to downstream packages.
|
||||
# tc: transitive closure
|
||||
tc_declarations, tc_types, tc_paths = set(), set(), {}
|
||||
tc_declarations_internal, tc_paths_internal = set(), {}
|
||||
|
||||
for dep in ctx.attr.deps:
|
||||
tc_declarations += dep.typescript.tc_declarations
|
||||
tc_types += dep.typescript.tc_types
|
||||
tc_paths.update(dep.typescript.tc_paths)
|
||||
|
||||
if dep not in ctx.attr.deps_use_internal:
|
||||
tc_declarations_internal += dep.typescript.tc_declarations
|
||||
tc_paths_internal.update(dep.typescript.tc_paths)
|
||||
|
||||
for dep in ctx.attr.deps:
|
||||
# Do this after the normal stuff so that the @internal transitive closure
|
||||
# will take precedence in the tc_paths_internal dict.
|
||||
if dep in ctx.attr.deps_use_internal:
|
||||
tc_declarations_internal += dep.typescript.internal.tc_declarations
|
||||
tc_paths_internal.update(dep.typescript.internal.tc_paths)
|
||||
|
||||
# Notice that the paths in the tsconfig.json should be relative to the file.
|
||||
tsconfig_to_workspace = "/".join(
|
||||
[".." for x in join_paths(ctx.configuration.bin_dir.path, ctx.label.package).split("/")])
|
||||
|
||||
# These two are package-relative.
|
||||
# normalize_path handles the case where root_dir/out_dir is set to ".".
|
||||
# TypeScript notoriously doesn't work with paths with /./ in the middle.
|
||||
root_dir = normalize_path(
|
||||
ctx.attr.root_dir or drop_dir(ctx.file.tsconfig.dirname, ctx.label.package))
|
||||
out_dir = normalize_path(ctx.attr.out_dir or root_dir)
|
||||
|
||||
# These correspond to keys in tsconfig.json.
|
||||
ts_files = [f for f in ctx.files.srcs if not f.short_path.endswith(".d.ts")]
|
||||
base_tsconfig = {
|
||||
"compilerOptions": {
|
||||
"rootDir": join_paths(
|
||||
tsconfig_to_workspace, source_roots[0], ctx.label.package, root_dir),
|
||||
"paths": {module: [join_paths(tsconfig_to_workspace, path)]
|
||||
for module, path in tc_paths_internal.items()},
|
||||
# Required to use paths.
|
||||
"baseUrl": ".",
|
||||
|
||||
"skipLibCheck": True,
|
||||
"stripInternal": True,
|
||||
|
||||
"declaration": not ctx.attr.is_leaf,
|
||||
|
||||
# All dependencies should be loaded with "deps", so we don't need the
|
||||
# node-style resolution nor @types resolution. This also improves
|
||||
# consistency in sandboxed and unsandboxed environments.
|
||||
"moduleResolution": "classic",
|
||||
"typeRoots": [],
|
||||
|
||||
# TODO: fix the path encoded in .js.map
|
||||
"sourceMap": ctx.attr.source_map,
|
||||
"inlineSourceMap": ctx.attr.inline_source_map,
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipMetadataEmit": ctx.attr.is_leaf,
|
||||
},
|
||||
"files": ([join_paths(tsconfig_to_workspace, f.path) for f in ctx.files.srcs] +
|
||||
[join_paths(tsconfig_to_workspace, path) for path in tc_types]),
|
||||
}
|
||||
tsc_action_args = dict(
|
||||
ctx = ctx,
|
||||
inputs = ctx.files.srcs + list(tc_declarations_internal),
|
||||
ts_files = ts_files,
|
||||
root_dir = root_dir,
|
||||
out_dir = out_dir,
|
||||
)
|
||||
|
||||
has_source_map = ctx.attr.source_map and not ctx.attr.inline_source_map
|
||||
is_tsc_wrapped = "bootstrap" not in ctx.executable.compiler.path
|
||||
is_leaf = ctx.attr.is_leaf
|
||||
|
||||
tsconfig = _tsconfig_action(
|
||||
ctx = ctx,
|
||||
prefix = "",
|
||||
input_tsconfig = ctx.file.tsconfig,
|
||||
mixin_tsconfig = _tsconfig_with(base_tsconfig, {"outDir": out_dir}),
|
||||
)
|
||||
gen_js, gen_d_ts, gen_meta, gen_js_map = _tsc_action(
|
||||
prefix = "",
|
||||
gen_config = (True, not is_leaf, not is_leaf and is_tsc_wrapped, has_source_map),
|
||||
tsconfig = tsconfig,
|
||||
**tsc_action_args
|
||||
)
|
||||
|
||||
tsconfig_esm = _tsconfig_action(
|
||||
ctx = ctx,
|
||||
prefix = "esm",
|
||||
input_tsconfig = ctx.file.tsconfig,
|
||||
mixin_tsconfig = _tsconfig_with(base_tsconfig, {
|
||||
"module": "es2015",
|
||||
"outDir": join_paths(out_dir, "esm"),
|
||||
}),
|
||||
)
|
||||
gen_js_esm, gen_d_ts_esm, gen_meta_esm, gen_js_map_esm = _tsc_action(
|
||||
prefix = "esm",
|
||||
gen_config = (True, not is_leaf, not is_leaf and is_tsc_wrapped, has_source_map),
|
||||
tsconfig = tsconfig_esm,
|
||||
**tsc_action_args
|
||||
)
|
||||
|
||||
tsconfig_internal = _tsconfig_action(
|
||||
ctx = ctx,
|
||||
prefix = "internal",
|
||||
input_tsconfig = ctx.file.tsconfig,
|
||||
mixin_tsconfig = _tsconfig_with(base_tsconfig, {
|
||||
"stripInternal": False,
|
||||
"sourceMap": False,
|
||||
"inlineSourceMap": False,
|
||||
"outDir": join_paths(out_dir, "internal"),
|
||||
}),
|
||||
)
|
||||
|
||||
if not is_leaf:
|
||||
_, gen_d_ts_internal, gen_meta_internal, _ = _tsc_action(
|
||||
prefix = "internal",
|
||||
gen_config = (False, True, is_tsc_wrapped, False),
|
||||
tsconfig = tsconfig_internal,
|
||||
**tsc_action_args
|
||||
)
|
||||
else:
|
||||
gen_d_ts_internal, gen_meta_internal = [], []
|
||||
|
||||
module_name = ctx.attr.module_name or ctx.label.name
|
||||
abs_package = join_paths(ctx.configuration.bin_dir.path, ctx.label.workspace_root,
|
||||
ctx.label.package, out_dir)
|
||||
return struct(
|
||||
files = set(gen_js),
|
||||
runfiles = ctx.runfiles(
|
||||
files = gen_js + gen_js_map,
|
||||
collect_default = True,
|
||||
collect_data = True,
|
||||
),
|
||||
typescript = struct(
|
||||
files = gen_js,
|
||||
module_name = module_name,
|
||||
# The rootDir relative to the current package.
|
||||
package_dir = out_dir,
|
||||
# The declarations of the current module
|
||||
declarations = gen_d_ts,
|
||||
source_maps = gen_js_map,
|
||||
metadata = gen_meta,
|
||||
# All declaration files in the transitive closure
|
||||
tc_declarations = tc_declarations + gen_d_ts,
|
||||
# Paths to declaration files to be loaded explicitly with "files",
|
||||
# relative to workspace.
|
||||
tc_types = tc_types,
|
||||
# Mapping to declaration files to be loaded implicitly with "paths",
|
||||
# relative to workspace.
|
||||
tc_paths = _merge_dict(tc_paths, {
|
||||
# We simply assume an index.d.ts exists. TypeScript will give the
|
||||
# same "Cannot find module" if it isn't true.
|
||||
module_name: join_paths(abs_package, "index"),
|
||||
module_name + "/*": join_paths(abs_package, "*"),
|
||||
}),
|
||||
# The @internal variant of the declaration files. Optional.
|
||||
internal = struct(
|
||||
package_dir = join_paths(out_dir, "internal"),
|
||||
declarations = gen_d_ts_internal,
|
||||
tc_declarations = tc_declarations_internal + gen_d_ts_internal,
|
||||
tc_paths = _merge_dict(tc_paths_internal, {
|
||||
module_name: join_paths(abs_package, "internal/index"),
|
||||
module_name + "/*": join_paths(abs_package, "internal/*"),
|
||||
}),
|
||||
),
|
||||
# This struct exists solely for npm_package to work simpler.
|
||||
# TypeScript-agnostic tools should use javascript_esm.
|
||||
esm = struct(
|
||||
files = gen_js_esm,
|
||||
source_maps = gen_js_map_esm,
|
||||
declarations = gen_d_ts_esm,
|
||||
metadata = gen_meta_esm,
|
||||
module_name = module_name,
|
||||
package_dir = join_paths(out_dir, "esm"),
|
||||
),
|
||||
is_leaf = is_leaf,
|
||||
),
|
||||
nodejs = struct(),
|
||||
javascript = struct(
|
||||
files = gen_js + gen_js_map,
|
||||
source_maps = gen_js_map,
|
||||
module_name = module_name,
|
||||
package_dir = out_dir,
|
||||
),
|
||||
javascript_esm = struct(
|
||||
files = gen_js_esm,
|
||||
source_maps = gen_js_map_esm,
|
||||
module_name = module_name,
|
||||
package_dir = join_paths(out_dir, "esm"),
|
||||
),
|
||||
)
|
||||
|
||||
def _tsconfig_with(base, compiler_options={}, angular_compiler_options={}):
|
||||
return _merge_dict(base, {
|
||||
"compilerOptions": _merge_dict(base["compilerOptions"], compiler_options),
|
||||
"angularCompilerOptions": _merge_dict(
|
||||
base["angularCompilerOptions"], angular_compiler_options),
|
||||
})
|
||||
|
||||
def _tsconfig_action(*, ctx, prefix, input_tsconfig, mixin_tsconfig):
|
||||
tsconfig = ctx.new_file(ctx.label.name + ("_" + prefix if prefix else "") + "_tsconfig.json")
|
||||
|
||||
target_name = "{}{}".format(ctx.label, " ({})".format(prefix) if prefix else "")
|
||||
ctx.action(
|
||||
progress_message = "Generating tsconfig.json for {}".format(target_name),
|
||||
inputs = [input_tsconfig],
|
||||
outputs = [tsconfig],
|
||||
executable = ctx.executable._merge_tsconfig,
|
||||
arguments = ["--file", input_tsconfig.path, pseudo_json_encode(mixin_tsconfig), "--out",
|
||||
tsconfig.path],
|
||||
)
|
||||
|
||||
return tsconfig
|
||||
|
||||
def _tsc_action(*, ctx, inputs, ts_files, root_dir, out_dir, prefix, gen_config, tsconfig):
|
||||
real_out_dir = join_paths(out_dir, prefix)
|
||||
has_js, has_d_ts, has_meta, has_js_map = gen_config
|
||||
|
||||
gen_js = map_files(ctx, ts_files, root_dir, real_out_dir, ".js") if has_js else []
|
||||
gen_d_ts = map_files(ctx, ts_files, root_dir, real_out_dir, ".d.ts") if has_d_ts else []
|
||||
gen_meta = map_files(ctx, ts_files, root_dir, real_out_dir, ".metadata.json") if has_meta else []
|
||||
gen_js_map = map_files(ctx, ts_files, root_dir, real_out_dir, ".js.map") if has_js_map else []
|
||||
|
||||
is_tsc_wrapped = "bootstrap" not in ctx.executable.compiler.path
|
||||
target_name = "{}{}".format(ctx.label, " ({})".format(prefix) if prefix else "")
|
||||
ctx.action(
|
||||
progress_message = "Compiling TypeScript {}".format(target_name),
|
||||
mnemonic = "TypeScriptCompile",
|
||||
inputs = inputs + [tsconfig],
|
||||
outputs = gen_js + gen_d_ts + gen_meta + gen_js_map,
|
||||
executable = ctx.executable.compiler,
|
||||
arguments = ["@@" + tsconfig.path] if is_tsc_wrapped else ["--project", tsconfig.path],
|
||||
execution_requirements = {"supports-workers": "1"} if is_tsc_wrapped else {},
|
||||
)
|
||||
|
||||
return gen_js, gen_d_ts, gen_meta, gen_js_map
|
||||
|
||||
_ts_library = rule(
|
||||
_ts_library_impl,
|
||||
attrs = {
|
||||
"compiler": attr.label(
|
||||
default = Label("//:tsc-wrapped_bin"),
|
||||
executable = True,
|
||||
single_file = True,
|
||||
),
|
||||
"tsconfig": attr.label(allow_files=True, single_file=True, mandatory=True),
|
||||
"srcs": attr.label_list(
|
||||
allow_files = FileType([
|
||||
".ts", # This also implicitly accepts .d.ts.
|
||||
".tsx",
|
||||
]),
|
||||
mandatory = True,
|
||||
),
|
||||
"deps": attr.label_list(providers=["typescript"]),
|
||||
"deps_use_internal": attr.label_list(providers=["typescript"]),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
"module_name": attr.string(),
|
||||
"root_dir": attr.string(default=""),
|
||||
"out_dir": attr.string(default=""),
|
||||
"source_map": attr.bool(default=True),
|
||||
"inline_source_map": attr.bool(default=False),
|
||||
"is_leaf": attr.bool(default=False),
|
||||
|
||||
"_merge_tsconfig": attr.label(
|
||||
default = Label("//build_defs/tools:merge_tsconfig"),
|
||||
executable = True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def ts_library(*, name, **kwargs):
|
||||
_ts_library(name=name, **kwargs)
|
||||
pick_provider(name=name + "_esm", srcs=[":" + name], providers=["javascript_esm.files"])
|
||||
|
||||
def _merge_dict(a, *args):
|
||||
ret = dict(a)
|
||||
for d in args:
|
||||
ret.update(d)
|
||||
return ret
|
||||
|
||||
|
||||
def _ts_ext_library_impl(ctx):
|
||||
"""ts_ext_library
|
||||
|
||||
Basically a nodejs_module with d.ts files.
|
||||
"""
|
||||
|
||||
module_name = ctx.attr.module_name or ctx.label.name
|
||||
# The d.ts files of a ts_ext_library rule lives in the source directory
|
||||
# instead of the bin directory
|
||||
abs_package = join_paths(ctx.label.workspace_root, ctx.label.package, ctx.attr.root_dir)
|
||||
|
||||
if (not ctx.attr.entry_point and len(ctx.files.declarations) == 1 and
|
||||
ctx.files.declarations[0].path.endswith(".d.ts")):
|
||||
entry_point_file, entry_point_relative_path = ctx.files.declarations, ""
|
||||
else:
|
||||
entry_point_file, entry_point_relative_path = pick_file_in_dir(
|
||||
ctx.files.declarations, ctx.label, ctx.attr.entry_point or "index.d.ts")
|
||||
|
||||
tc_declarations, tc_types, tc_paths = set(), set(), {}
|
||||
tc_declarations_internal, tc_paths_internal = set(), {}
|
||||
|
||||
for dep in ctx.attr.deps:
|
||||
tc_declarations += dep.typescript.tc_declarations
|
||||
tc_types += dep.typescript.tc_types
|
||||
tc_paths.update(dep.typescript.tc_paths)
|
||||
|
||||
return struct(
|
||||
files = set(ctx.files.srcs),
|
||||
runfiles = ctx.runfiles(
|
||||
files = ctx.files.srcs,
|
||||
collect_data = True,
|
||||
collect_default = True,
|
||||
),
|
||||
typescript = struct(
|
||||
module_name = module_name,
|
||||
package_dir = ctx.attr.root_dir,
|
||||
declarations = ctx.files.declarations,
|
||||
tc_declarations = tc_declarations + ctx.files.declarations,
|
||||
tc_types =
|
||||
tc_types + set([join_paths(entry_point_file.path, entry_point_relative_path)]),
|
||||
tc_paths = _merge_dict(tc_paths, {
|
||||
module_name: join_paths(entry_point_file.path, entry_point_relative_path),
|
||||
module_name + "/*": join_paths(abs_package, "*"),
|
||||
} if not ctx.attr.ambient else {}),
|
||||
is_leaf = False,
|
||||
),
|
||||
nodejs = struct(),
|
||||
javascript = struct(
|
||||
files = ctx.files.srcs,
|
||||
module_name = module_name,
|
||||
package_dir = ctx.attr.root_dir,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
ts_ext_library = rule(
|
||||
_ts_ext_library_impl,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(allow_files=True),
|
||||
"deps": attr.label_list(providers=["typescript"], cfg=DATA_CFG),
|
||||
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
|
||||
"declarations": attr.label_list(allow_files=FileType([".d.ts"]), mandatory=True),
|
||||
"ambient": attr.bool(mandatory=True),
|
||||
"module_name": attr.string(),
|
||||
"root_dir": attr.string(default=""),
|
||||
"entry_point": attr.string(),
|
||||
}
|
||||
)
|
||||
|
151
build_defs/utils.bzl
Normal file
151
build_defs/utils.bzl
Normal file
@ -0,0 +1,151 @@
|
||||
def join_paths(*paths):
|
||||
segments = []
|
||||
for path in paths:
|
||||
segments += path.split("/")
|
||||
|
||||
return "/".join([seg for seg in segments if seg])
|
||||
|
||||
|
||||
def normalize_path(path):
|
||||
segments = []
|
||||
for seg in path.split("/"):
|
||||
if seg == "..":
|
||||
if segments:
|
||||
segments.pop()
|
||||
else:
|
||||
segments.append("..")
|
||||
elif seg and seg != ".":
|
||||
segments.append(seg)
|
||||
|
||||
return "/".join(segments)
|
||||
|
||||
|
||||
def drop_dir(path, directory):
|
||||
if not path.startswith(directory):
|
||||
fail("Path \"%s\" does not reside in directory \"%s\"" % (path, directory))
|
||||
if directory:
|
||||
return path[len(directory) + 1:]
|
||||
else:
|
||||
return path
|
||||
|
||||
|
||||
def map_files(ctx, files, root_dir, out_dir, ext=None):
|
||||
"""Creates a list of output files given directory and the extension.
|
||||
|
||||
root_dir and out_dir are specified relative to the package.
|
||||
"""
|
||||
ret = []
|
||||
for f in files:
|
||||
path_in_package = drop_dir(f.short_path, ctx.label.package)
|
||||
if ext != None:
|
||||
path_in_package_without_ext = path_in_package[:path_in_package.rfind(".")]
|
||||
filename = join_paths(out_dir, drop_dir(path_in_package_without_ext, root_dir) + ext)
|
||||
else:
|
||||
filename = join_paths(out_dir, drop_dir(path_in_package, root_dir))
|
||||
ret.append(ctx.new_file(filename))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def pick_file(files, base_label, path, attr=None):
|
||||
"""Returns the file within the target which matches the specified package-
|
||||
relative path.
|
||||
|
||||
Args:
|
||||
files: A list of files to check.
|
||||
base_label: The label of the package of interest.
|
||||
path: The path of the needed file relative to `base_label`.
|
||||
"""
|
||||
|
||||
match, remainder_path = pick_file_in_dir(files, base_label, path, attr)
|
||||
|
||||
if remainder_path:
|
||||
fail("Cannot find '{}' in package '{}'. Available files are: \n{}".format(
|
||||
path, base_label.package, "\n".join([f.path for f in files])), attr)
|
||||
|
||||
return match
|
||||
|
||||
|
||||
def pick_file_in_dir(files, base_label, path, attr=None):
|
||||
"""Finds the file or directory containing the file specified by
|
||||
`base_label` and `path` inside the list `files`.
|
||||
Returns a tuple (file, path) where file is the found file or directory
|
||||
and path is the remaining path to be appended in order to get to the actual
|
||||
file.
|
||||
|
||||
This is used to perform best-effort analysis-phase checking of a file
|
||||
addressed by a path within a target, e.g. for entry_point in nodejs_binary.
|
||||
|
||||
Args:
|
||||
files: A list of files to check.
|
||||
base_label: The label of the package of interest.
|
||||
path: The path of the needed file relative to `base_label`.
|
||||
"""
|
||||
|
||||
short_path = join_paths(base_label.package, path)
|
||||
|
||||
# We assume that a parent directory of an input file is not also an input
|
||||
# file.
|
||||
matches = [f for f in files
|
||||
if short_path == f.short_path or short_path.startswith(f.short_path + "/")]
|
||||
|
||||
if not matches:
|
||||
fail("Cannot find '{}' in package '{}'. Available files are: \n{}".format(
|
||||
path, base_label.package, "\n".join([f.path for f in files])), attr)
|
||||
|
||||
if len(matches) > 1:
|
||||
# We neglect cases whether two files in a target come from different
|
||||
# workspaces.
|
||||
fail("Multiple matches of '{}' found in package '{}'!".format(path, base_label.package), attr)
|
||||
|
||||
remainder_path = short_path[len(matches[0].short_path) + 1:]
|
||||
|
||||
return (matches[0], remainder_path)
|
||||
|
||||
|
||||
def pseudo_json_encode(dictionary):
|
||||
# We abuse the fact that str() of a dict is almost a valid JSON object, and
|
||||
# that we do not use characters requiring escaping.
|
||||
return str(dictionary).replace("True", "true").replace("False", "false")
|
||||
|
||||
|
||||
def _pick_provider_impl(ctx):
|
||||
"""pick_provider
|
||||
|
||||
Rule that serves as an escape hatch for complex Skylark-based rules to easily
|
||||
expose multiple targets that correspond to a subset of the provider. This
|
||||
allows genrules or macros to build upon these targets.
|
||||
|
||||
Args:
|
||||
srcs: The targets to pick the provider from.
|
||||
providers: A list of dotted keys to pick under the target. The files in that
|
||||
path will be collected.
|
||||
"""
|
||||
files = set()
|
||||
|
||||
for src in ctx.attr.srcs:
|
||||
for provider in ctx.attr.providers:
|
||||
keys = provider.split(".")
|
||||
out = src
|
||||
for k in keys:
|
||||
if not hasattr(out, k):
|
||||
fail("Target {} does not have provider \"{}\"".format(src.label, provider), "srcs")
|
||||
out = getattr(out, k)
|
||||
files += out
|
||||
|
||||
return struct(
|
||||
files = files,
|
||||
runfiles = ctx.runfiles(
|
||||
files = list(files),
|
||||
collect_default = True,
|
||||
collect_data = True,
|
||||
),
|
||||
)
|
||||
|
||||
pick_provider = rule(
|
||||
_pick_provider_impl,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(mandatory=True),
|
||||
"providers": attr.string_list(mandatory=True),
|
||||
},
|
||||
)
|
11
circle.yml
Normal file
11
circle.yml
Normal file
@ -0,0 +1,11 @@
|
||||
machine:
|
||||
node:
|
||||
version: 5.4.1
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
|
||||
test:
|
||||
override:
|
||||
- gulp lint
|
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
1496
gulpfile.js
1496
gulpfile.js
File diff suppressed because it is too large
Load Diff
12
ibazel
Executable file
12
ibazel
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
ibazel_path=$(dirname "${0}")/bazel-bin/tools/ibazel/ibazel_bin
|
||||
|
||||
if ! [[ -x "${ibazel_path}" ]]; then
|
||||
echo "Building ibazel..."
|
||||
bazel build tools/ibazel:ibazel_bin
|
||||
fi
|
||||
|
||||
"${ibazel_path}" "$@"
|
@ -1,86 +0,0 @@
|
||||
// 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,82 +0,0 @@
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
|
||||
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)
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
frameworks: ['dart-unittest', 'dart-evalcache'],
|
||||
|
||||
files: [
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-init.dart', included: true},
|
||||
// Unit test files needs to be included.
|
||||
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: 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: 'modules/**/test/**/static_assets/**', included: false, watched: false},
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/dart/**/packages/**',
|
||||
'modules/angular1_router/**'
|
||||
],
|
||||
|
||||
karmaDartImports: {
|
||||
guinness: 'package:guinness/guinness_html.dart'
|
||||
},
|
||||
|
||||
// Map packages to the correct urls where Karma serves them.
|
||||
proxies: proxyPaths,
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
browsers: ['DartiumWithWebPlatform'],
|
||||
|
||||
port: 9877,
|
||||
|
||||
plugins: [
|
||||
require('karma-dart'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-sauce-launcher'),
|
||||
require('./karma-dart-evalcache')(packageSources)
|
||||
]
|
||||
});
|
||||
};
|
@ -1,38 +1,51 @@
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
var internalAngularReporter = require('./tools/karma/reporter.js');
|
||||
var ibazelWatcher = require('./tools/karma/ibazel_watcher.js');
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
frameworks: ['jasmine', 'ibazel_watcher'],
|
||||
|
||||
files: [
|
||||
// Sources and specs.
|
||||
// Loaded through the System loader, in `test-main.js`.
|
||||
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/**/*.js', included: false, watched: true},
|
||||
{pattern: 'modules/@angular/**/*.js.map', included: false, watched: true},
|
||||
|
||||
'node_modules/es6-shim/es6-shim.js',
|
||||
'node_modules/core-js/client/core.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/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/proxy.js',
|
||||
'node_modules/zone.js/dist/sync-test.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
'node_modules/zone.js/dist/async-test.js',
|
||||
'node_modules/zone.js/dist/fake-async-test.js',
|
||||
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'modules/angular2/src/testing/shims_for_IE.js',
|
||||
'shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'tools/build/file2modulename.js',
|
||||
// 'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
|
||||
{pattern: 'modules/empty.*', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
|
||||
],
|
||||
|
||||
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/js/dev/es5/angular2/examples/**', 'dist/angular1_router.js'],
|
||||
exclude: [
|
||||
'modules/@angular/**/e2e_test/**',
|
||||
'modules/@angular/examples/**',
|
||||
'modules/@angular/compiler-cli/**',
|
||||
'modules/angular1_router.js',
|
||||
'modules/@angular/platform-browser/testing/e2e_util.js'
|
||||
],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
|
||||
@ -42,8 +55,8 @@ module.exports = function(config) {
|
||||
'karma-sauce-launcher',
|
||||
'karma-chrome-launcher',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-dart',
|
||||
internalAngularReporter
|
||||
internalAngularReporter,
|
||||
ibazelWatcher
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
@ -53,11 +66,12 @@ module.exports = function(config) {
|
||||
reporters: ['internal-angular'],
|
||||
sauceLabs: {
|
||||
testName: 'Angular2',
|
||||
retryLimit: 3,
|
||||
startConnect: false,
|
||||
recordVideo: false,
|
||||
recordScreenshots: false,
|
||||
options: {
|
||||
'selenium-version': '2.48.2',
|
||||
'selenium-version': '2.53.0',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
@ -67,19 +81,23 @@ module.exports = function(config) {
|
||||
browserStack: {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 1,
|
||||
retryLimit: 3,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout : 60000,
|
||||
browserDisconnectTolerance : 3,
|
||||
browserNoActivityTimeout : 60000,
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.MODE === 'saucelabs') {
|
||||
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
@ -89,7 +107,7 @@ module.exports = function(config) {
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
|
||||
if (process.env.MODE === 'browserstack') {
|
||||
if (process.env.CI_MODE.startsWith('browserstack')) {
|
||||
config.browserStack.build = buildId;
|
||||
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
}
|
||||
|
13
modules/@angular/common/index.ts
Normal file
13
modules/@angular/common/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './src/pipes';
|
||||
export * from './src/directives';
|
||||
export * from './src/location';
|
||||
export {NgLocalization} from './src/localization';
|
||||
export {CommonModule} from './src/common_module';
|
17
modules/@angular/common/package.json
Normal file
17
modules/@angular/common/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@angular/common",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"jsnext:main": "esm/index.js",
|
||||
"typings": "index.d.ts",
|
||||
"author": "angular",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
}
|
||||
}
|
17
modules/@angular/common/rollup.config.js
Normal file
17
modules/@angular/common/rollup.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/common/esm/index.js',
|
||||
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
|
||||
format: 'umd',
|
||||
moduleName: 'ng.common',
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Subject': 'Rx',
|
||||
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
|
||||
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
|
||||
'rxjs/Observable': 'Rx'
|
||||
},
|
||||
plugins: [
|
||||
// nodeResolve({ jsnext: true, main: true }),
|
||||
]
|
||||
}
|
59
modules/@angular/common/src/common_directives.ts
Normal file
59
modules/@angular/common/src/common_directives.ts
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Provider} from '@angular/core';
|
||||
|
||||
import {CORE_DIRECTIVES} from './directives/core_directives';
|
||||
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application. This includes core directives (e.g., NgIf and NgFor), and forms directives (e.g.,
|
||||
* NgModel).
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@Component` decorator.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Instead of writing:
|
||||
*
|
||||
* ```typescript
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, NgModel, NgForm} from
|
||||
* '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, NgModel, NgForm,
|
||||
* OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* one could import all the common directives at once:
|
||||
*
|
||||
* ```typescript
|
||||
* import {COMMON_DIRECTIVES} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [COMMON_DIRECTIVES, OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental Contains forms which are experimental.
|
||||
*/
|
||||
export const COMMON_DIRECTIVES: Provider[] = CORE_DIRECTIVES;
|
30
modules/@angular/common/src/common_module.ts
Normal file
30
modules/@angular/common/src/common_module.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {COMMON_DIRECTIVES} from './common_directives';
|
||||
import {NgLocaleLocalization, NgLocalization} from './localization';
|
||||
import {COMMON_PIPES} from './pipes/common_pipes';
|
||||
|
||||
// Note: This does not contain the location providers,
|
||||
// as they need some platform specific implementations to work.
|
||||
/**
|
||||
* The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ...
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@NgModule({
|
||||
declarations: [COMMON_DIRECTIVES, COMMON_PIPES],
|
||||
exports: [COMMON_DIRECTIVES, COMMON_PIPES],
|
||||
providers: [
|
||||
{provide: NgLocalization, useClass: NgLocaleLocalization},
|
||||
],
|
||||
})
|
||||
export class CommonModule {
|
||||
}
|
20
modules/@angular/common/src/directives.ts
Normal file
20
modules/@angular/common/src/directives.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Common directives shipped with Angular.
|
||||
*/
|
||||
export {NgClass} from './directives/ng_class';
|
||||
export {NgFor} from './directives/ng_for';
|
||||
export {NgIf} from './directives/ng_if';
|
||||
export {NgPlural, NgPluralCase} from './directives/ng_plural';
|
||||
export {NgStyle} from './directives/ng_style';
|
||||
export {NgSwitch, NgSwitchCase, NgSwitchDefault} from './directives/ng_switch';
|
||||
export {NgTemplateOutlet} from './directives/ng_template_outlet';
|
72
modules/@angular/common/src/directives/core_directives.ts
Normal file
72
modules/@angular/common/src/directives/core_directives.ts
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
import {NgClass} from './ng_class';
|
||||
import {NgFor} from './ng_for';
|
||||
import {NgIf} from './ng_if';
|
||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||
import {NgStyle} from './ng_style';
|
||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
||||
import {NgTemplateOutlet} from './ng_template_outlet';
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@Component` annotation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
|
||||
*
|
||||
* Instead of writing:
|
||||
*
|
||||
* ```typescript
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* one could import all the core directives at once:
|
||||
*
|
||||
* ```typescript
|
||||
* import {CORE_DIRECTIVES} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [CORE_DIRECTIVES, OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export const CORE_DIRECTIVES: Type<any>[] = [
|
||||
NgClass,
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgTemplateOutlet,
|
||||
NgStyle,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
NgPlural,
|
||||
NgPluralCase,
|
||||
];
|
189
modules/@angular/common/src/directives/ng_class.ts
Normal file
189
modules/@angular/common/src/directives/ng_class.ts
Normal file
@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper, isListLikeIterable} from '../facade/collection';
|
||||
import {isArray, isPresent, isString} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
||||
* an expression's evaluation result.
|
||||
*
|
||||
* The result of an expression evaluation is interpreted differently depending on type of
|
||||
* the expression evaluation result:
|
||||
* - `string` - all the CSS classes listed in a string (space delimited) are added
|
||||
* - `Array` - all the CSS classes (Array elements) are added
|
||||
* - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
|
||||
* evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
|
||||
* is added - otherwise it is removed.
|
||||
*
|
||||
* While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
|
||||
* or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
|
||||
* all the CSS class names in a template.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgClass} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'toggle-button',
|
||||
* inputs: ['isDisabled'],
|
||||
* template: `
|
||||
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
|
||||
* (click)="toggle(!isOn)">
|
||||
* Click me!
|
||||
* </div>`,
|
||||
* styles: [`
|
||||
* .button {
|
||||
* width: 120px;
|
||||
* border: medium solid black;
|
||||
* }
|
||||
*
|
||||
* .active {
|
||||
* background-color: red;
|
||||
* }
|
||||
*
|
||||
* .disabled {
|
||||
* color: gray;
|
||||
* border: medium solid gray;
|
||||
* }
|
||||
* `],
|
||||
* directives: [NgClass]
|
||||
* })
|
||||
* class ToggleButton {
|
||||
* isOn = false;
|
||||
* isDisabled = false;
|
||||
*
|
||||
* toggle(newState) {
|
||||
* if (!this.isDisabled) {
|
||||
* this.isOn = newState;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngClass]'})
|
||||
export class NgClass implements DoCheck {
|
||||
private _iterableDiffer: IterableDiffer;
|
||||
private _keyValueDiffer: KeyValueDiffer;
|
||||
private _initialClasses: string[] = [];
|
||||
private _rawClass: string[]|Set<string>;
|
||||
|
||||
constructor(
|
||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
|
||||
@Input('class')
|
||||
set initialClasses(v: string) {
|
||||
this._applyInitialClasses(true);
|
||||
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
|
||||
this._applyInitialClasses(false);
|
||||
this._applyClasses(this._rawClass, false);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngClass(v: string|string[]|Set<string>|{[key: string]: any}) {
|
||||
this._cleanupClasses(this._rawClass);
|
||||
|
||||
if (isString(v)) {
|
||||
v = (<string>v).split(' ');
|
||||
}
|
||||
|
||||
this._rawClass = <string[]|Set<string>>v;
|
||||
this._iterableDiffer = null;
|
||||
this._keyValueDiffer = null;
|
||||
if (isPresent(v)) {
|
||||
if (isListLikeIterable(v)) {
|
||||
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
|
||||
} else {
|
||||
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (isPresent(this._iterableDiffer)) {
|
||||
var changes = this._iterableDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
this._applyIterableChanges(changes);
|
||||
}
|
||||
}
|
||||
if (isPresent(this._keyValueDiffer)) {
|
||||
var changes = this._keyValueDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[key: string]: any}): void {
|
||||
this._applyClasses(rawClassVal, true);
|
||||
this._applyInitialClasses(false);
|
||||
}
|
||||
|
||||
private _applyKeyValueChanges(changes: any): void {
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
||||
if (record.previousValue) {
|
||||
this._toggleClass(record.key, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, false); });
|
||||
}
|
||||
|
||||
private _applyInitialClasses(isCleanup: boolean) {
|
||||
this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
|
||||
}
|
||||
|
||||
private _applyClasses(
|
||||
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
|
||||
if (isPresent(rawClassVal)) {
|
||||
if (isArray(rawClassVal)) {
|
||||
(<string[]>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
||||
} else if (rawClassVal instanceof Set) {
|
||||
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
||||
} else {
|
||||
StringMapWrapper.forEach(
|
||||
<{[k: string]: any}>rawClassVal, (expVal: any, className: string) => {
|
||||
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleClass(className: string, enabled: boolean): void {
|
||||
className = className.trim();
|
||||
if (className.length > 0) {
|
||||
if (className.indexOf(' ') > -1) {
|
||||
var classes = className.split(/\s+/g);
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
|
||||
}
|
||||
} else {
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
171
modules/@angular/common/src/directives/ng_for.ts
Normal file
171
modules/@angular/common/src/directives/ng_for.ts
Normal file
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {getTypeNameForDebugging, isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
export class NgForRow {
|
||||
constructor(public $implicit: any, public index: number, public count: number) {}
|
||||
|
||||
get first(): boolean { return this.index === 0; }
|
||||
|
||||
get last(): boolean { return this.index === this.count - 1; }
|
||||
|
||||
get even(): boolean { return this.index % 2 === 0; }
|
||||
|
||||
get odd(): boolean { return !this.even; }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
|
||||
* each instantiated template inherits from the outer context with the given loop variable set
|
||||
* to the current item from the iterable.
|
||||
*
|
||||
* ### Local Variables
|
||||
*
|
||||
* `NgFor` provides several exported values that can be aliased to local variables:
|
||||
*
|
||||
* * `index` will be set to the current loop iteration for each template context.
|
||||
* * `first` will be set to a boolean value indicating whether the item is the first one in the
|
||||
* iteration.
|
||||
* * `last` will be set to a boolean value indicating whether the item is the last one in the
|
||||
* iteration.
|
||||
* * `even` will be set to a boolean value indicating whether this item has an even index.
|
||||
* * `odd` will be set to a boolean value indicating whether this item has an odd index.
|
||||
*
|
||||
* ### Change Propagation
|
||||
*
|
||||
* When the contents of the iterator changes, `NgFor` makes the corresponding changes to the DOM:
|
||||
*
|
||||
* * When an item is added, a new instance of the template is added to the DOM.
|
||||
* * When an item is removed, its template instance is removed from the DOM.
|
||||
* * When items are reordered, their respective templates are reordered in the DOM.
|
||||
* * Otherwise, the DOM element for that item will remain the same.
|
||||
*
|
||||
* Angular uses object identity to track insertions and deletions within the iterator and reproduce
|
||||
* those changes in the DOM. This has important implications for animations and any stateful
|
||||
* controls
|
||||
* (such as `<input>` elements which accept user input) that are present. Inserted rows can be
|
||||
* animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state such
|
||||
* as user input.
|
||||
*
|
||||
* It is possible for the identities of elements in the iterator to change while the data does not.
|
||||
* This can happen, for example, if the iterator produced from an RPC to the server, and that
|
||||
* RPC is re-run. Even if the data hasn't changed, the second response will produce objects with
|
||||
* different identities, and Angular will tear down the entire DOM and rebuild it (as if all old
|
||||
* elements were deleted and all new elements inserted). This is an expensive operation and should
|
||||
* be avoided if possible.
|
||||
*
|
||||
* To customize the default tracking algorithm, `NgFor` supports `trackBy` option.
|
||||
* `trackBy` takes a function which has two arguments: `index` and `item`.
|
||||
* If `trackBy` is given, Angular tracks changes by the return value of the function.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<li *ngFor="let item of items; let i = index; trackBy: trackByFn">...</li>`
|
||||
* - `<li template="ngFor let item of items; let i = index; trackBy: trackByFn">...</li>`
|
||||
*
|
||||
* With `<template>` element:
|
||||
*
|
||||
* ```
|
||||
* <template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
* <li>...</li>
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
|
||||
* example.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngFor][ngForOf]'})
|
||||
export class NgFor implements DoCheck, OnChanges {
|
||||
@Input() ngForOf: any;
|
||||
@Input() ngForTrackBy: TrackByFn;
|
||||
|
||||
private _differ: IterableDiffer;
|
||||
|
||||
constructor(
|
||||
private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
|
||||
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
@Input()
|
||||
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
||||
if (isPresent(value)) {
|
||||
this._templateRef = value;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if ('ngForOf' in changes) {
|
||||
// React on ngForOf changes only once all inputs have been initialized
|
||||
const value = changes['ngForOf'].currentValue;
|
||||
if (isBlank(this._differ) && isPresent(value)) {
|
||||
try {
|
||||
this._differ = this._iterableDiffers.find(value).create(this._cdr, this.ngForTrackBy);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
const changes = this._differ.diff(this.ngForOf);
|
||||
if (isPresent(changes)) this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: DefaultIterableDiffer) {
|
||||
const insertTuples: RecordViewTuple[] = [];
|
||||
changes.forEachOperation(
|
||||
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||
if (item.previousIndex == null) {
|
||||
let view = this._viewContainer.createEmbeddedView(
|
||||
this._templateRef, new NgForRow(null, null, null), currentIndex);
|
||||
let tuple = new RecordViewTuple(item, view);
|
||||
insertTuples.push(tuple);
|
||||
} else if (currentIndex == null) {
|
||||
this._viewContainer.remove(adjustedPreviousIndex);
|
||||
} else {
|
||||
let view = this._viewContainer.get(adjustedPreviousIndex);
|
||||
this._viewContainer.move(view, currentIndex);
|
||||
let tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
|
||||
insertTuples.push(tuple);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < insertTuples.length; i++) {
|
||||
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
|
||||
view.context.$implicit = record.item;
|
||||
}
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
constructor(public record: any, public view: EmbeddedViewRef<NgForRow>) {}
|
||||
}
|
56
modules/@angular/common/src/directives/ng_if.ts
Normal file
56
modules/@angular/common/src/directives/ng_if.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isBlank} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||
*
|
||||
* If the expression assigned to `ngIf` evaluates to a false value then the element
|
||||
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* <div *ngIf="errorCount > 0" class="error">
|
||||
* <!-- Error message displayed when the errorCount property on the current context is greater
|
||||
* than 0. -->
|
||||
* {{errorCount}} errors detected
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div *ngIf="condition">...</div>`
|
||||
* - `<div template="ngIf condition">...</div>`
|
||||
* - `<template [ngIf]="condition"><div>...</div></template>`
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngIf]'})
|
||||
export class NgIf {
|
||||
private _prevCondition: boolean = null;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngIf(newCondition: any) {
|
||||
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
|
||||
this._prevCondition = true;
|
||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
||||
} else if (!newCondition && (isBlank(this._prevCondition) || this._prevCondition)) {
|
||||
this._prevCondition = false;
|
||||
this._viewContainer.clear();
|
||||
}
|
||||
}
|
||||
}
|
110
modules/@angular/common/src/directives/ng_plural.ts
Normal file
110
modules/@angular/common/src/directives/ng_plural.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, Directive, Host, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
|
||||
import {SwitchView} from './ng_switch';
|
||||
|
||||
|
||||
/**
|
||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
||||
*
|
||||
* To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
||||
* to a
|
||||
* switch expression.
|
||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
||||
* expression.
|
||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||
* matches the switch expression exactly.
|
||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
||||
* value matches aren't found and the value maps to its category for the defined locale.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* // best practice is to define the locale at the application level
|
||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngPlural]="value">
|
||||
* <template ngPluralCase="=0">there is nothing</template>
|
||||
* <template ngPluralCase="=1">there is one</template>
|
||||
* <template ngPluralCase="few">there are a few</template>
|
||||
* <template ngPluralCase="other">there is some number</template>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [NgPlural, NgPluralCase]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPlural]'})
|
||||
export class NgPlural {
|
||||
private _switchValue: number;
|
||||
private _activeView: SwitchView;
|
||||
private _caseViews: {[k: string]: SwitchView} = {};
|
||||
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
@Input()
|
||||
set ngPlural(value: number) {
|
||||
this._switchValue = value;
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
var key =
|
||||
getPluralCategory(this._switchValue, Object.keys(this._caseViews), this._localization);
|
||||
this._activateView(this._caseViews[key]);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_clearViews() {
|
||||
if (isPresent(this._activeView)) this._activeView.destroy();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateView(view: SwitchView) {
|
||||
if (!isPresent(view)) return;
|
||||
this._activeView = view;
|
||||
this._activeView.create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
constructor(
|
||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
|
||||
ngPlural.addCase(value, new SwitchView(viewContainer, template));
|
||||
}
|
||||
}
|
112
modules/@angular/common/src/directives/ng_style.ts
Normal file
112
modules/@angular/common/src/directives/ng_style.ts
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
||||
*
|
||||
* An expression assigned to the `ngStyle` property must evaluate to an object and the
|
||||
* corresponding element styles are updated based on changes to this object. Style names to update
|
||||
* are taken from the object's keys, and values - from the corresponding object's values.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
|
||||
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
|
||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgStyle} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'ngStyle-example',
|
||||
* template: `
|
||||
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
||||
* Change style of this text!
|
||||
* </h1>
|
||||
*
|
||||
* <hr>
|
||||
*
|
||||
* <label>Italic: <input type="checkbox" (change)="changeStyle($event)"></label>
|
||||
* <label>Bold: <input type="checkbox" (change)="changeWeight($event)"></label>
|
||||
* <label>Size: <input type="text" [value]="size" (change)="size = $event.target.value"></label>
|
||||
* `,
|
||||
* directives: [NgStyle]
|
||||
* })
|
||||
* export class NgStyleExample {
|
||||
* style = 'normal';
|
||||
* weight = 'normal';
|
||||
* size = '20px';
|
||||
*
|
||||
* changeStyle($event: any) {
|
||||
* this.style = $event.target.checked ? 'italic' : 'normal';
|
||||
* }
|
||||
*
|
||||
* changeWeight($event: any) {
|
||||
* this.weight = $event.target.checked ? 'bold' : 'normal';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
|
||||
* based on the `style` property's value changes.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngStyle]'})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_ngStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
|
||||
constructor(
|
||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
@Input()
|
||||
set ngStyle(v: {[key: string]: string}) {
|
||||
this._ngStyle = v;
|
||||
if (isBlank(this._differ) && isPresent(v)) {
|
||||
this._differ = this._differs.find(this._ngStyle).create(null);
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._ngStyle);
|
||||
if (isPresent(changes)) {
|
||||
this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: any): void {
|
||||
changes.forEachRemovedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
}
|
||||
|
||||
private _setStyle(name: string, val: string): void {
|
||||
const nameParts = name.split('.');
|
||||
const nameToSet = nameParts[0];
|
||||
const valToSet = isPresent(val) && nameParts.length === 2 ? `${val}${nameParts[1]}` : val;
|
||||
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, nameToSet, valToSet);
|
||||
}
|
||||
}
|
220
modules/@angular/common/src/directives/ng_switch.ts
Normal file
220
modules/@angular/common/src/directives/ng_switch.ts
Normal file
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, normalizeBlank} from '../facade/lang';
|
||||
|
||||
const _CASE_DEFAULT = new Object();
|
||||
|
||||
export class SwitchView {
|
||||
constructor(
|
||||
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
||||
|
||||
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||
|
||||
destroy(): void { this._viewContainerRef.clear(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
|
||||
*
|
||||
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
|
||||
* preserved at the location as specified in the template.
|
||||
*
|
||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
||||
* obtained from the evaluated switch expression. In other words, you define a container element
|
||||
* (where you place the directive with a switch expression on the
|
||||
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
|
||||
* place a `[ngSwitchCase]` attribute per element.
|
||||
*
|
||||
* The `ngSwitchCase` property is used to inform `NgSwitch` which element to display when the
|
||||
* expression is evaluated. If a matching expression is not found via a `ngSwitchCase` property
|
||||
* then an element with the `ngSwitchDefault` attribute is displayed.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngSwitch]="value">
|
||||
* <p *ngSwitchCase="'init'">increment to start</p>
|
||||
* <p *ngSwitchCase="0">0, increment again</p>
|
||||
* <p *ngSwitchCase="1">1, increment again</p>
|
||||
* <p *ngSwitchCase="2">2, stop incrementing</p>
|
||||
* <p *ngSwitchDefault>> 2, STOP!</p>
|
||||
* </div>
|
||||
*
|
||||
* <!-- alternate syntax -->
|
||||
*
|
||||
* <p [ngSwitch]="value">
|
||||
* <template ngSwitchCase="init">increment to start</template>
|
||||
* <template [ngSwitchCase]="0">0, increment again</template>
|
||||
* <template [ngSwitchCase]="1">1, increment again</template>
|
||||
* <template [ngSwitchCase]="2">2, stop incrementing</template>
|
||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
||||
* </p>
|
||||
* `,
|
||||
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngSwitch]'})
|
||||
export class NgSwitch {
|
||||
private _switchValue: any;
|
||||
private _useDefault: boolean = false;
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
|
||||
@Input()
|
||||
set ngSwitch(value: any) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViews();
|
||||
|
||||
// Add the ViewContainers matching the value (with a fallback to default)
|
||||
this._useDefault = false;
|
||||
var views = this._valueViews.get(value);
|
||||
if (isBlank(views)) {
|
||||
this._useDefault = true;
|
||||
views = normalizeBlank(this._valueViews.get(_CASE_DEFAULT));
|
||||
}
|
||||
this._activateViews(views);
|
||||
|
||||
this._switchValue = value;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_onCaseValueChanged(oldCase: any, newCase: any, view: SwitchView): void {
|
||||
this._deregisterView(oldCase, view);
|
||||
this._registerView(newCase, view);
|
||||
|
||||
if (oldCase === this._switchValue) {
|
||||
view.destroy();
|
||||
ListWrapper.remove(this._activeViews, view);
|
||||
} else if (newCase === this._switchValue) {
|
||||
if (this._useDefault) {
|
||||
this._useDefault = false;
|
||||
this._emptyAllActiveViews();
|
||||
}
|
||||
view.create();
|
||||
this._activeViews.push(view);
|
||||
}
|
||||
|
||||
// Switch to default when there is no more active ViewContainers
|
||||
if (this._activeViews.length === 0 && !this._useDefault) {
|
||||
this._useDefault = true;
|
||||
this._activateViews(this._valueViews.get(_CASE_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_emptyAllActiveViews(): void {
|
||||
var activeContainers = this._activeViews;
|
||||
for (var i = 0; i < activeContainers.length; i++) {
|
||||
activeContainers[i].destroy();
|
||||
}
|
||||
this._activeViews = [];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateViews(views: SwitchView[]): void {
|
||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
||||
if (isPresent(views)) {
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
views[i].create();
|
||||
}
|
||||
this._activeViews = views;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_registerView(value: any, view: SwitchView): void {
|
||||
var views = this._valueViews.get(value);
|
||||
if (isBlank(views)) {
|
||||
views = [];
|
||||
this._valueViews.set(value, views);
|
||||
}
|
||||
views.push(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_deregisterView(value: any, view: SwitchView): void {
|
||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||
if (value === _CASE_DEFAULT) return;
|
||||
var views = this._valueViews.get(value);
|
||||
if (views.length == 1) {
|
||||
this._valueViews.delete(value);
|
||||
} else {
|
||||
ListWrapper.remove(views, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the sub-tree when the `ngSwitchCase` expression evaluates to the same value as the
|
||||
* enclosing switch expression.
|
||||
*
|
||||
* If multiple match expression match the switch expression value, all of them are displayed.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchCase]'})
|
||||
export class NgSwitchCase {
|
||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||
/** @internal */
|
||||
_value: any = _CASE_DEFAULT;
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
private _switch: NgSwitch;
|
||||
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
this._switch = ngSwitch;
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchCase(value: any) {
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default case statements are displayed when no match expression matches the switch expression
|
||||
* value.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchDefault]'})
|
||||
export class NgSwitchDefault {
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() sswitch: NgSwitch) {
|
||||
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
|
||||
}
|
||||
}
|
52
modules/@angular/common/src/directives/ng_template_outlet.ts
Normal file
52
modules/@angular/common/src/directives/ng_template_outlet.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||
* available within the `TemplateRef`.
|
||||
*
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* ```
|
||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
||||
* [ngOutletContext]="objectExpression">
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngTemplateOutlet]'})
|
||||
export class NgTemplateOutlet implements OnChanges {
|
||||
private _viewRef: EmbeddedViewRef<any>;
|
||||
private _context: Object;
|
||||
private _templateRef: TemplateRef<any>;
|
||||
|
||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
@Input()
|
||||
set ngOutletContext(context: Object) { this._context = context; }
|
||||
|
||||
@Input()
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
||||
|
||||
ngOnChanges() {
|
||||
if (this._viewRef) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
||||
if (this._templateRef) {
|
||||
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
|
||||
}
|
||||
}
|
||||
}
|
1
modules/@angular/common/src/facade
Symbolic link
1
modules/@angular/common/src/facade
Symbolic link
@ -0,0 +1 @@
|
||||
../../facade/src
|
420
modules/@angular/common/src/localization.ts
Normal file
420
modules/@angular/common/src/localization.ts
Normal file
@ -0,0 +1,420 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, LOCALE_ID} from '@angular/core';
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
|
||||
|
||||
|
||||
/**
|
||||
* Returns the plural category for a given value.
|
||||
* - "=value" when the case exists,
|
||||
* - the plural category otherwise
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getPluralCategory(
|
||||
value: number, cases: string[], ngLocalization: NgLocalization): string {
|
||||
const nbCase = `=${value}`;
|
||||
|
||||
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plural case based on the locale
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Injectable()
|
||||
export class NgLocaleLocalization extends NgLocalization {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) { super(); }
|
||||
|
||||
getPluralCategory(value: any): string {
|
||||
const plural = getPluralCase(this._locale, value);
|
||||
|
||||
switch (plural) {
|
||||
case Plural.Zero:
|
||||
return 'zero';
|
||||
case Plural.One:
|
||||
return 'one';
|
||||
case Plural.Two:
|
||||
return 'two';
|
||||
case Plural.Few:
|
||||
return 'few';
|
||||
case Plural.Many:
|
||||
return 'many';
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is generated code DO NOT MODIFY
|
||||
// see angular2/script/cldr/gen_plural_rules.js
|
||||
|
||||
/** @experimental */
|
||||
export enum Plural {
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Few,
|
||||
Many,
|
||||
Other
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plural case based on the locale
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export function getPluralCase(locale: string, nLike: number | string): Plural {
|
||||
// TODO(vicb): lazy compute
|
||||
if (typeof nLike === 'string') {
|
||||
nLike = parseInt(<string>nLike, 10);
|
||||
}
|
||||
const n: number = nLike as number;
|
||||
const nDecimal = n.toString().replace(/^[^.]*\.?/, '');
|
||||
const i = Math.floor(Math.abs(n));
|
||||
const v = nDecimal.length;
|
||||
const f = parseInt(nDecimal, 10);
|
||||
const t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0;
|
||||
|
||||
const lang = locale.split('_')[0].toLowerCase();
|
||||
|
||||
switch (lang) {
|
||||
case 'af':
|
||||
case 'asa':
|
||||
case 'az':
|
||||
case 'bem':
|
||||
case 'bez':
|
||||
case 'bg':
|
||||
case 'brx':
|
||||
case 'ce':
|
||||
case 'cgg':
|
||||
case 'chr':
|
||||
case 'ckb':
|
||||
case 'ee':
|
||||
case 'el':
|
||||
case 'eo':
|
||||
case 'es':
|
||||
case 'eu':
|
||||
case 'fo':
|
||||
case 'fur':
|
||||
case 'gsw':
|
||||
case 'ha':
|
||||
case 'haw':
|
||||
case 'hu':
|
||||
case 'jgo':
|
||||
case 'jmc':
|
||||
case 'ka':
|
||||
case 'kk':
|
||||
case 'kkj':
|
||||
case 'kl':
|
||||
case 'ks':
|
||||
case 'ksb':
|
||||
case 'ky':
|
||||
case 'lb':
|
||||
case 'lg':
|
||||
case 'mas':
|
||||
case 'mgo':
|
||||
case 'ml':
|
||||
case 'mn':
|
||||
case 'nb':
|
||||
case 'nd':
|
||||
case 'ne':
|
||||
case 'nn':
|
||||
case 'nnh':
|
||||
case 'nyn':
|
||||
case 'om':
|
||||
case 'or':
|
||||
case 'os':
|
||||
case 'ps':
|
||||
case 'rm':
|
||||
case 'rof':
|
||||
case 'rwk':
|
||||
case 'saq':
|
||||
case 'seh':
|
||||
case 'sn':
|
||||
case 'so':
|
||||
case 'sq':
|
||||
case 'ta':
|
||||
case 'te':
|
||||
case 'teo':
|
||||
case 'tk':
|
||||
case 'tr':
|
||||
case 'ug':
|
||||
case 'uz':
|
||||
case 'vo':
|
||||
case 'vun':
|
||||
case 'wae':
|
||||
case 'xog':
|
||||
if (n === 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'agq':
|
||||
case 'bas':
|
||||
case 'cu':
|
||||
case 'dav':
|
||||
case 'dje':
|
||||
case 'dua':
|
||||
case 'dyo':
|
||||
case 'ebu':
|
||||
case 'ewo':
|
||||
case 'guz':
|
||||
case 'kam':
|
||||
case 'khq':
|
||||
case 'ki':
|
||||
case 'kln':
|
||||
case 'kok':
|
||||
case 'ksf':
|
||||
case 'lrc':
|
||||
case 'lu':
|
||||
case 'luo':
|
||||
case 'luy':
|
||||
case 'mer':
|
||||
case 'mfe':
|
||||
case 'mgh':
|
||||
case 'mua':
|
||||
case 'mzn':
|
||||
case 'nmg':
|
||||
case 'nus':
|
||||
case 'qu':
|
||||
case 'rn':
|
||||
case 'rw':
|
||||
case 'sbp':
|
||||
case 'twq':
|
||||
case 'vai':
|
||||
case 'yav':
|
||||
case 'yue':
|
||||
case 'zgh':
|
||||
case 'ak':
|
||||
case 'ln':
|
||||
case 'mg':
|
||||
case 'pa':
|
||||
case 'ti':
|
||||
if (n === Math.floor(n) && n >= 0 && n <= 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'am':
|
||||
case 'as':
|
||||
case 'bn':
|
||||
case 'fa':
|
||||
case 'gu':
|
||||
case 'hi':
|
||||
case 'kn':
|
||||
case 'mr':
|
||||
case 'zu':
|
||||
if (i === 0 || n === 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'ar':
|
||||
if (n === 0) return Plural.Zero;
|
||||
if (n === 1) return Plural.One;
|
||||
if (n === 2) return Plural.Two;
|
||||
if (n % 100 === Math.floor(n % 100) && n % 100 >= 3 && n % 100 <= 10) return Plural.Few;
|
||||
if (n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 99) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'ast':
|
||||
case 'ca':
|
||||
case 'de':
|
||||
case 'en':
|
||||
case 'et':
|
||||
case 'fi':
|
||||
case 'fy':
|
||||
case 'gl':
|
||||
case 'it':
|
||||
case 'nl':
|
||||
case 'sv':
|
||||
case 'sw':
|
||||
case 'ur':
|
||||
case 'yi':
|
||||
if (i === 1 && v === 0) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'be':
|
||||
if (n % 10 === 1 && !(n % 100 === 11)) return Plural.One;
|
||||
if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 4 &&
|
||||
!(n % 100 >= 12 && n % 100 <= 14))
|
||||
return Plural.Few;
|
||||
if (n % 10 === 0 || n % 10 === Math.floor(n % 10) && n % 10 >= 5 && n % 10 <= 9 ||
|
||||
n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 14)
|
||||
return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'br':
|
||||
if (n % 10 === 1 && !(n % 100 === 11 || n % 100 === 71 || n % 100 === 91)) return Plural.One;
|
||||
if (n % 10 === 2 && !(n % 100 === 12 || n % 100 === 72 || n % 100 === 92)) return Plural.Two;
|
||||
if (n % 10 === Math.floor(n % 10) && (n % 10 >= 3 && n % 10 <= 4 || n % 10 === 9) &&
|
||||
!(n % 100 >= 10 && n % 100 <= 19 || n % 100 >= 70 && n % 100 <= 79 ||
|
||||
n % 100 >= 90 && n % 100 <= 99))
|
||||
return Plural.Few;
|
||||
if (!(n === 0) && n % 1e6 === 0) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'bs':
|
||||
case 'hr':
|
||||
case 'sr':
|
||||
if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11))
|
||||
return Plural.One;
|
||||
if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 &&
|
||||
!(i % 100 >= 12 && i % 100 <= 14) ||
|
||||
f % 10 === Math.floor(f % 10) && f % 10 >= 2 && f % 10 <= 4 &&
|
||||
!(f % 100 >= 12 && f % 100 <= 14))
|
||||
return Plural.Few;
|
||||
return Plural.Other;
|
||||
case 'cs':
|
||||
case 'sk':
|
||||
if (i === 1 && v === 0) return Plural.One;
|
||||
if (i === Math.floor(i) && i >= 2 && i <= 4 && v === 0) return Plural.Few;
|
||||
if (!(v === 0)) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'cy':
|
||||
if (n === 0) return Plural.Zero;
|
||||
if (n === 1) return Plural.One;
|
||||
if (n === 2) return Plural.Two;
|
||||
if (n === 3) return Plural.Few;
|
||||
if (n === 6) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'da':
|
||||
if (n === 1 || !(t === 0) && (i === 0 || i === 1)) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'dsb':
|
||||
case 'hsb':
|
||||
if (v === 0 && i % 100 === 1 || f % 100 === 1) return Plural.One;
|
||||
if (v === 0 && i % 100 === 2 || f % 100 === 2) return Plural.Two;
|
||||
if (v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 3 && i % 100 <= 4 ||
|
||||
f % 100 === Math.floor(f % 100) && f % 100 >= 3 && f % 100 <= 4)
|
||||
return Plural.Few;
|
||||
return Plural.Other;
|
||||
case 'ff':
|
||||
case 'fr':
|
||||
case 'hy':
|
||||
case 'kab':
|
||||
if (i === 0 || i === 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'fil':
|
||||
if (v === 0 && (i === 1 || i === 2 || i === 3) ||
|
||||
v === 0 && !(i % 10 === 4 || i % 10 === 6 || i % 10 === 9) ||
|
||||
!(v === 0) && !(f % 10 === 4 || f % 10 === 6 || f % 10 === 9))
|
||||
return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'ga':
|
||||
if (n === 1) return Plural.One;
|
||||
if (n === 2) return Plural.Two;
|
||||
if (n === Math.floor(n) && n >= 3 && n <= 6) return Plural.Few;
|
||||
if (n === Math.floor(n) && n >= 7 && n <= 10) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'gd':
|
||||
if (n === 1 || n === 11) return Plural.One;
|
||||
if (n === 2 || n === 12) return Plural.Two;
|
||||
if (n === Math.floor(n) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) return Plural.Few;
|
||||
return Plural.Other;
|
||||
case 'gv':
|
||||
if (v === 0 && i % 10 === 1) return Plural.One;
|
||||
if (v === 0 && i % 10 === 2) return Plural.Two;
|
||||
if (v === 0 &&
|
||||
(i % 100 === 0 || i % 100 === 20 || i % 100 === 40 || i % 100 === 60 || i % 100 === 80))
|
||||
return Plural.Few;
|
||||
if (!(v === 0)) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'he':
|
||||
if (i === 1 && v === 0) return Plural.One;
|
||||
if (i === 2 && v === 0) return Plural.Two;
|
||||
if (v === 0 && !(n >= 0 && n <= 10) && n % 10 === 0) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'is':
|
||||
if (t === 0 && i % 10 === 1 && !(i % 100 === 11) || !(t === 0)) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'ksh':
|
||||
if (n === 0) return Plural.Zero;
|
||||
if (n === 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'kw':
|
||||
case 'naq':
|
||||
case 'se':
|
||||
case 'smn':
|
||||
if (n === 1) return Plural.One;
|
||||
if (n === 2) return Plural.Two;
|
||||
return Plural.Other;
|
||||
case 'lag':
|
||||
if (n === 0) return Plural.Zero;
|
||||
if ((i === 0 || i === 1) && !(n === 0)) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'lt':
|
||||
if (n % 10 === 1 && !(n % 100 >= 11 && n % 100 <= 19)) return Plural.One;
|
||||
if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 9 &&
|
||||
!(n % 100 >= 11 && n % 100 <= 19))
|
||||
return Plural.Few;
|
||||
if (!(f === 0)) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'lv':
|
||||
case 'prg':
|
||||
if (n % 10 === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 19 ||
|
||||
v === 2 && f % 100 === Math.floor(f % 100) && f % 100 >= 11 && f % 100 <= 19)
|
||||
return Plural.Zero;
|
||||
if (n % 10 === 1 && !(n % 100 === 11) || v === 2 && f % 10 === 1 && !(f % 100 === 11) ||
|
||||
!(v === 2) && f % 10 === 1)
|
||||
return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'mk':
|
||||
if (v === 0 && i % 10 === 1 || f % 10 === 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'mt':
|
||||
if (n === 1) return Plural.One;
|
||||
if (n === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 2 && n % 100 <= 10)
|
||||
return Plural.Few;
|
||||
if (n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 19) return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'pl':
|
||||
if (i === 1 && v === 0) return Plural.One;
|
||||
if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 &&
|
||||
!(i % 100 >= 12 && i % 100 <= 14))
|
||||
return Plural.Few;
|
||||
if (v === 0 && !(i === 1) && i % 10 === Math.floor(i % 10) && i % 10 >= 0 && i % 10 <= 1 ||
|
||||
v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 5 && i % 10 <= 9 ||
|
||||
v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 12 && i % 100 <= 14)
|
||||
return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'pt':
|
||||
if (n === Math.floor(n) && n >= 0 && n <= 2 && !(n === 2)) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'ro':
|
||||
if (i === 1 && v === 0) return Plural.One;
|
||||
if (!(v === 0) || n === 0 ||
|
||||
!(n === 1) && n % 100 === Math.floor(n % 100) && n % 100 >= 1 && n % 100 <= 19)
|
||||
return Plural.Few;
|
||||
return Plural.Other;
|
||||
case 'ru':
|
||||
case 'uk':
|
||||
if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return Plural.One;
|
||||
if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 &&
|
||||
!(i % 100 >= 12 && i % 100 <= 14))
|
||||
return Plural.Few;
|
||||
if (v === 0 && i % 10 === 0 ||
|
||||
v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 5 && i % 10 <= 9 ||
|
||||
v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 11 && i % 100 <= 14)
|
||||
return Plural.Many;
|
||||
return Plural.Other;
|
||||
case 'shi':
|
||||
if (i === 0 || n === 1) return Plural.One;
|
||||
if (n === Math.floor(n) && n >= 2 && n <= 10) return Plural.Few;
|
||||
return Plural.Other;
|
||||
case 'si':
|
||||
if (n === 0 || n === 1 || i === 0 && f === 1) return Plural.One;
|
||||
return Plural.Other;
|
||||
case 'sl':
|
||||
if (v === 0 && i % 100 === 1) return Plural.One;
|
||||
if (v === 0 && i % 100 === 2) return Plural.Two;
|
||||
if (v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 3 && i % 100 <= 4 || !(v === 0))
|
||||
return Plural.Few;
|
||||
return Plural.Other;
|
||||
case 'tzm':
|
||||
if (n === Math.floor(n) && n >= 0 && n <= 1 || n === Math.floor(n) && n >= 11 && n <= 99)
|
||||
return Plural.One;
|
||||
return Plural.Other;
|
||||
default:
|
||||
return Plural.Other;
|
||||
}
|
||||
}
|
13
modules/@angular/common/src/location.ts
Normal file
13
modules/@angular/common/src/location.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './location/platform_location';
|
||||
export * from './location/location_strategy';
|
||||
export * from './location/hash_location_strategy';
|
||||
export * from './location/path_location_strategy';
|
||||
export * from './location/location';
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, Optional} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
import {Location} from './location';
|
||||
import {APP_BASE_HREF, LocationStrategy} from './location_strategy';
|
||||
import {LocationChangeListener, PlatformLocation} from './platform_location';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||
* {@link Location} service to represent its state in the
|
||||
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
|
||||
* of the browser's URL.
|
||||
*
|
||||
* For instance, if you call `location.go('/foo')`, the browser's URL will become
|
||||
* `example.com#/foo`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component, NgModule} from '@angular/core';
|
||||
* import {
|
||||
* LocationStrategy,
|
||||
* HashLocationStrategy
|
||||
* } from '@angular/common';
|
||||
*
|
||||
* @NgModule({
|
||||
* providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]
|
||||
* })
|
||||
* class AppModule {}
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Injectable()
|
||||
export class HashLocationStrategy extends LocationStrategy {
|
||||
private _baseHref: string = '';
|
||||
constructor(
|
||||
private _platformLocation: PlatformLocation,
|
||||
@Optional() @Inject(APP_BASE_HREF) _baseHref?: string) {
|
||||
super();
|
||||
if (isPresent(_baseHref)) {
|
||||
this._baseHref = _baseHref;
|
||||
}
|
||||
}
|
||||
|
||||
onPopState(fn: LocationChangeListener): void {
|
||||
this._platformLocation.onPopState(fn);
|
||||
this._platformLocation.onHashChange(fn);
|
||||
}
|
||||
|
||||
getBaseHref(): string { return this._baseHref; }
|
||||
|
||||
path(includeHash: boolean = false): string {
|
||||
// the hash value is always prefixed with a `#`
|
||||
// and if it is empty then it will stay empty
|
||||
var path = this._platformLocation.hash;
|
||||
if (!isPresent(path)) path = '#';
|
||||
|
||||
return path.length > 0 ? path.substring(1) : path;
|
||||
}
|
||||
|
||||
prepareExternalUrl(internal: string): string {
|
||||
var url = Location.joinWithSlash(this._baseHref, internal);
|
||||
return url.length > 0 ? ('#' + url) : url;
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
this._platformLocation.pushState(state, title, url);
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
this._platformLocation.replaceState(state, title, url);
|
||||
}
|
||||
|
||||
forward(): void { this._platformLocation.forward(); }
|
||||
|
||||
back(): void { this._platformLocation.back(); }
|
||||
}
|
195
modules/@angular/common/src/location/location.ts
Normal file
195
modules/@angular/common/src/location/location.ts
Normal file
@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, Injectable} from '@angular/core';
|
||||
|
||||
import {LocationStrategy} from './location_strategy';
|
||||
|
||||
|
||||
/**
|
||||
* `Location` is a service that applications can use to interact with a browser's URL.
|
||||
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
|
||||
* to the URL's path or the URL's hash segment.
|
||||
*
|
||||
* Note: it's better to use {@link Router#navigate} service to trigger route changes. Use
|
||||
* `Location` only if you need to interact with or create normalized URLs outside of
|
||||
* routing.
|
||||
*
|
||||
* `Location` is responsible for normalizing the URL against the application's base href.
|
||||
* A normalized URL is absolute from the URL host, includes the application's base href, and has no
|
||||
* trailing slash:
|
||||
* - `/my/app/user/123` is normalized
|
||||
* - `my/app/user/123` **is not** normalized
|
||||
* - `/my/app/user/123/` **is not** normalized
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {Location} from '@angular/common';
|
||||
*
|
||||
* @Component({selector: 'app-component'})
|
||||
* class AppCmp {
|
||||
* constructor(location: Location) {
|
||||
* location.go('/foo');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Injectable()
|
||||
export class Location {
|
||||
/** @internal */
|
||||
_subject: EventEmitter<any> = new EventEmitter();
|
||||
/** @internal */
|
||||
_baseHref: string;
|
||||
|
||||
/** @internal */
|
||||
_platformStrategy: LocationStrategy;
|
||||
|
||||
constructor(platformStrategy: LocationStrategy) {
|
||||
this._platformStrategy = platformStrategy;
|
||||
var browserBaseHref = this._platformStrategy.getBaseHref();
|
||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||
this._platformStrategy.onPopState(
|
||||
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normalized URL path.
|
||||
*/
|
||||
// TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is
|
||||
// removed.
|
||||
path(includeHash: boolean = false): string {
|
||||
return this.normalize(this._platformStrategy.path(includeHash));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the given path and compares to the current normalized path.
|
||||
*/
|
||||
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||
return this.path() == this.normalize(path + Location.normalizeQueryParams(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string representing a URL, returns the normalized URL path without leading or
|
||||
* trailing slashes
|
||||
*/
|
||||
normalize(url: string): string {
|
||||
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string representing a URL, returns the platform-specific external URL path.
|
||||
* If the given URL doesn't begin with a leading slash (`'/'`), this method adds one
|
||||
* before normalizing. This method will also add a hash if `HashLocationStrategy` is
|
||||
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
||||
*/
|
||||
prepareExternalUrl(url: string): string {
|
||||
if (url.length > 0 && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
return this._platformStrategy.prepareExternalUrl(url);
|
||||
}
|
||||
|
||||
// TODO: rename this method to pushState
|
||||
/**
|
||||
* Changes the browsers URL to the normalized version of the given URL, and pushes a
|
||||
* new item onto the platform's history.
|
||||
*/
|
||||
go(path: string, query: string = ''): void {
|
||||
this._platformStrategy.pushState(null, '', path, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the browsers URL to the normalized version of the given URL, and replaces
|
||||
* the top item on the platform's history stack.
|
||||
*/
|
||||
replaceState(path: string, query: string = ''): void {
|
||||
this._platformStrategy.replaceState(null, '', path, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates forward in the platform's history.
|
||||
*/
|
||||
forward(): void { this._platformStrategy.forward(); }
|
||||
|
||||
/**
|
||||
* Navigates back in the platform's history.
|
||||
*/
|
||||
back(): void { this._platformStrategy.back(); }
|
||||
|
||||
/**
|
||||
* Subscribe to the platform's `popState` events.
|
||||
*/
|
||||
subscribe(
|
||||
onNext: (value: any) => void, onThrow: (exception: any) => void = null,
|
||||
onReturn: () => void = null): Object {
|
||||
return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string of url parameters, prepend with '?' if needed, otherwise return parameters as
|
||||
* is.
|
||||
*/
|
||||
public static normalizeQueryParams(params: string): string {
|
||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given 2 parts of a url, join them with a slash if needed.
|
||||
*/
|
||||
public static joinWithSlash(start: string, end: string): string {
|
||||
if (start.length == 0) {
|
||||
return end;
|
||||
}
|
||||
if (end.length == 0) {
|
||||
return start;
|
||||
}
|
||||
var slashes = 0;
|
||||
if (start.endsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
if (end.startsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
if (slashes == 2) {
|
||||
return start + end.substring(1);
|
||||
}
|
||||
if (slashes == 1) {
|
||||
return start + end;
|
||||
}
|
||||
return start + '/' + end;
|
||||
}
|
||||
|
||||
/**
|
||||
* If url has a trailing slash, remove it, otherwise return url as is.
|
||||
*/
|
||||
public static stripTrailingSlash(url: string): string {
|
||||
if (/\/$/g.test(url)) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
function _stripBaseHref(baseHref: string, url: string): string {
|
||||
if (baseHref.length > 0 && url.startsWith(baseHref)) {
|
||||
return url.substring(baseHref.length);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function _stripIndexHtml(url: string): string {
|
||||
if (/\/index.html$/g.test(url)) {
|
||||
// '/index.html'.length == 11
|
||||
return url.substring(0, url.length - 11);
|
||||
}
|
||||
return url;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user