mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Compare commits
607 Commits
v1.7.8
...
travis-mod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
febf300fdd | ||
|
|
0eab022654 | ||
|
|
c190c74df9 | ||
|
|
6137633412 | ||
|
|
c2cb4f7c48 | ||
|
|
225dc0b69f | ||
|
|
e0825d1f47 | ||
|
|
b4ecfa5a96 | ||
|
|
3321382c62 | ||
|
|
e4a4cd1ba0 | ||
|
|
7901011d34 | ||
|
|
f313129e85 | ||
|
|
fb5a798920 | ||
|
|
6104718892 | ||
|
|
021736f7c5 | ||
|
|
66bd9d1fc4 | ||
|
|
07d40e7fbf | ||
|
|
16b651139a | ||
|
|
b47ce33e9e | ||
|
|
4996448d68 | ||
|
|
da1baa055f | ||
|
|
3e294789b5 | ||
|
|
45408e9589 | ||
|
|
cbb9b4415e | ||
|
|
e3328a4034 | ||
|
|
4e68c23c87 | ||
|
|
fcc02c4200 | ||
|
|
d24e7be811 | ||
|
|
746fd3a669 | ||
|
|
031c7731a4 | ||
|
|
fa62654b9c | ||
|
|
fd2d448b7e | ||
|
|
997fb2b298 | ||
|
|
9c9f332ea0 | ||
|
|
1861fa4355 | ||
|
|
79a539ffb4 | ||
|
|
0608b26364 | ||
|
|
15e5b9709d | ||
|
|
67741b4bfe | ||
|
|
45c6bfa86f | ||
|
|
4f68fc7daa | ||
|
|
206046b271 | ||
|
|
43894f5149 | ||
|
|
6390815c5f | ||
|
|
e8b94ecbd0 | ||
|
|
51feb8f925 | ||
|
|
1c15deadc0 | ||
|
|
2aa1f27367 | ||
|
|
275601601a | ||
|
|
c22c09546e | ||
|
|
6bc778df32 | ||
|
|
451c60ec20 | ||
|
|
d6a18ae7ee | ||
|
|
b62faa19a6 | ||
|
|
50ee729ea2 | ||
|
|
bb17839bc2 | ||
|
|
5a6ae540f9 | ||
|
|
1935716660 | ||
|
|
add601795f | ||
|
|
8e34328466 | ||
|
|
c3e9c94833 | ||
|
|
dd19bafa37 | ||
|
|
74af42c176 | ||
|
|
a6308bf8f8 | ||
|
|
e1dc0105d2 | ||
|
|
ce210e4f16 | ||
|
|
e483e9bc2c | ||
|
|
b0c3859699 | ||
|
|
e6d1b2d82e | ||
|
|
d4345bb254 | ||
|
|
975f9bdcdb | ||
|
|
a969a9c557 | ||
|
|
6500b421ce | ||
|
|
0641167b96 | ||
|
|
0d03374dab | ||
|
|
765d8e739d | ||
|
|
0bd318de53 | ||
|
|
aa6b51edc0 | ||
|
|
2c2e5309fd | ||
|
|
b716bc4e3a | ||
|
|
bda400634c | ||
|
|
b01243ac3c | ||
|
|
89902a6919 | ||
|
|
80308a41a6 | ||
|
|
47cc2262e1 | ||
|
|
f7c5154490 | ||
|
|
cba4b2a4cd | ||
|
|
bdabf6a4e6 | ||
|
|
7896224384 | ||
|
|
3209cda975 | ||
|
|
38501a0b93 | ||
|
|
e60d236b25 | ||
|
|
044896e708 | ||
|
|
fc4c260de4 | ||
|
|
d405917b4a | ||
|
|
22bbb3fcaf | ||
|
|
8b6c8eeaa9 | ||
|
|
58ddb59104 | ||
|
|
f26fe38c30 | ||
|
|
9317bb6e33 | ||
|
|
2693bae8ed | ||
|
|
8368539a93 | ||
|
|
8947400487 | ||
|
|
44eeb60529 | ||
|
|
2309cd80c6 | ||
|
|
684ab0c0a6 | ||
|
|
1f6b32a48e | ||
|
|
e4f0295ef1 | ||
|
|
85e39cf190 | ||
|
|
16c134f0f9 | ||
|
|
bcbff32716 | ||
|
|
809117ea73 | ||
|
|
af448ba484 | ||
|
|
89069784bb | ||
|
|
57cc6f7a40 | ||
|
|
cea728c7bc | ||
|
|
f6be8e5e28 | ||
|
|
c6f7ec36de | ||
|
|
3235b3c0d3 | ||
|
|
6ca5d434a4 | ||
|
|
eb27ae8fdc | ||
|
|
353a399f75 | ||
|
|
c718541a4e | ||
|
|
1f4372299a | ||
|
|
e640d0ec5c | ||
|
|
2e5acfe076 | ||
|
|
19f3c53c70 | ||
|
|
26902aec27 | ||
|
|
08feaf2b0a | ||
|
|
db1ed1c08f | ||
|
|
3c2562ca0e | ||
|
|
b1f1b8fae3 | ||
|
|
f1c874b202 | ||
|
|
4757e7353f | ||
|
|
249ac47b9c | ||
|
|
dc59913098 | ||
|
|
343e6ac8bc | ||
|
|
e729829174 | ||
|
|
40e3ee091b | ||
|
|
8ee2d78779 | ||
|
|
7c54812ecf | ||
|
|
3251051d20 | ||
|
|
81052830f2 | ||
|
|
7b2fd5dcd1 | ||
|
|
bbc9b35cb1 | ||
|
|
3aebb34f1d | ||
|
|
8065e5c64a | ||
|
|
b8e6f36a91 | ||
|
|
12d41aeb8c | ||
|
|
8c624bbda6 | ||
|
|
7ee1686cf4 | ||
|
|
8e181c1792 | ||
|
|
9569a8074d | ||
|
|
e8e4c8fdbc | ||
|
|
4793fc0d1c | ||
|
|
4d7cdb0556 | ||
|
|
037bbff17e | ||
|
|
358a73b98e | ||
|
|
32cbb5a0e8 | ||
|
|
3853d1297e | ||
|
|
1e398f999b | ||
|
|
3a0ea968f4 | ||
|
|
5283a132bc | ||
|
|
db1453f7c0 | ||
|
|
878a228a7d | ||
|
|
b33041c3ec | ||
|
|
c8a6ff38a0 | ||
|
|
ef67955c21 | ||
|
|
36a14b9b37 | ||
|
|
c17c725057 | ||
|
|
2b31f6c07a | ||
|
|
6a18cde782 | ||
|
|
32c5538fc5 | ||
|
|
57478d86c7 | ||
|
|
42107e6fea | ||
|
|
9aae3b8d7e | ||
|
|
34ec39507c | ||
|
|
304bb36bbc | ||
|
|
376a2de600 | ||
|
|
9e58bbca31 | ||
|
|
027a0694f7 | ||
|
|
7bc97a1241 | ||
|
|
bd2c253f99 | ||
|
|
f1efce7d1f | ||
|
|
844cc5a527 | ||
|
|
0e27b6f813 | ||
|
|
9219f54718 | ||
|
|
5a2272cab1 | ||
|
|
609607b096 | ||
|
|
e9657668a9 | ||
|
|
f18b38cde5 | ||
|
|
e6f1805df0 | ||
|
|
4da1b62542 | ||
|
|
6d12ef291b | ||
|
|
c8d5199815 | ||
|
|
cd8d397e63 | ||
|
|
5a9c099188 | ||
|
|
2137089a70 | ||
|
|
fbd02852a3 | ||
|
|
f2584ade24 | ||
|
|
50bfd14968 | ||
|
|
d867095f50 | ||
|
|
e51bf20e72 | ||
|
|
15f8a30cd4 | ||
|
|
b7c19695e7 | ||
|
|
b85cf2683c | ||
|
|
8df5970300 | ||
|
|
db265d471f | ||
|
|
0441e16bdb | ||
|
|
30a489535e | ||
|
|
ac88ece259 | ||
|
|
9b45c76744 | ||
|
|
059a5f83b7 | ||
|
|
cf5cd61995 | ||
|
|
a499cc5103 | ||
|
|
bebb4fb33b | ||
|
|
2c243ea5b7 | ||
|
|
f53100d8d3 | ||
|
|
acbe60cdf1 | ||
|
|
4c7f37e0f8 | ||
|
|
3ba696937c | ||
|
|
1647994471 | ||
|
|
674e09dc56 | ||
|
|
4c129d470f | ||
|
|
c630f01baa | ||
|
|
6018fc13b2 | ||
|
|
c4fc6cd0e2 | ||
|
|
eed8735238 | ||
|
|
8c0155e8bd | ||
|
|
b2d4412e59 | ||
|
|
4e3e45a88b | ||
|
|
af9b386d8a | ||
|
|
201b8a3bc6 | ||
|
|
a23f66d889 | ||
|
|
a1596bb63c | ||
|
|
19af145166 | ||
|
|
4ed81be0c6 | ||
|
|
1c62adcdb6 | ||
|
|
95f46930a5 | ||
|
|
76dd504589 | ||
|
|
938c69c816 | ||
|
|
d5fc402a89 | ||
|
|
065a7a1f1b | ||
|
|
ccadffea73 | ||
|
|
b3390ce201 | ||
|
|
6039f6c691 | ||
|
|
c65cdc8699 | ||
|
|
7603886e04 | ||
|
|
406a96e4d1 | ||
|
|
7d74d7d8f6 | ||
|
|
e98d8139bc | ||
|
|
8a47aab01d | ||
|
|
395b208a0c | ||
|
|
f6178c2f75 | ||
|
|
f1e04e5629 | ||
|
|
cc913a728a | ||
|
|
f8c179b153 | ||
|
|
bea46fb879 | ||
|
|
379de05a61 | ||
|
|
c2f222760a | ||
|
|
9aef3b7f1d | ||
|
|
c93bbbd302 | ||
|
|
009e5ce0c8 | ||
|
|
61bb4f53c0 | ||
|
|
b6dd5e445e | ||
|
|
6f4b77a440 | ||
|
|
463584ea10 | ||
|
|
fd8d603831 | ||
|
|
a1ec83b002 | ||
|
|
041d3f2843 | ||
|
|
0b22127906 | ||
|
|
0b0b507827 | ||
|
|
16de289942 | ||
|
|
02dc97e413 | ||
|
|
703eae72eb | ||
|
|
6a629e963c | ||
|
|
f242e60c1a | ||
|
|
6d335abe21 | ||
|
|
179b8a28b4 | ||
|
|
fc13328e2a | ||
|
|
b3f28fac64 | ||
|
|
f65af7a308 | ||
|
|
559f50a3e3 | ||
|
|
18b809314c | ||
|
|
aa1f819c0d | ||
|
|
5a8cecf499 | ||
|
|
79f362abee | ||
|
|
42db74b522 | ||
|
|
3309e9f53f | ||
|
|
ae3a017143 | ||
|
|
43d7a11ba8 | ||
|
|
02e12e17d6 | ||
|
|
4f838685d6 | ||
|
|
6339ba09f0 | ||
|
|
23c2e82c97 | ||
|
|
7138d3518e | ||
|
|
f852325906 | ||
|
|
3c9082ece3 | ||
|
|
98c77ffe18 | ||
|
|
b95d5f9f23 | ||
|
|
adf59d78d7 | ||
|
|
904ae3eab7 | ||
|
|
44d2309700 | ||
|
|
5dd6a3883a | ||
|
|
5f0a3fe1c1 | ||
|
|
bc3079332c | ||
|
|
3c0395b19f | ||
|
|
9e7b591c78 | ||
|
|
b6107a1198 | ||
|
|
2d94018f12 | ||
|
|
42f0268829 | ||
|
|
4812c380c4 | ||
|
|
0e833c155a | ||
|
|
9ebb1b17d1 | ||
|
|
3397cf053c | ||
|
|
6e73b5934e | ||
|
|
a5c49d89d5 | ||
|
|
1f9a92e6ad | ||
|
|
6280611aea | ||
|
|
20c0e6e12b | ||
|
|
d3ccc73796 | ||
|
|
53d3ac570e | ||
|
|
237022baae | ||
|
|
bccfd7bbbb | ||
|
|
597853cd6c | ||
|
|
1bd6568f94 | ||
|
|
4b2235aef2 | ||
|
|
faf1c26669 | ||
|
|
c385c08e2f | ||
|
|
ab7e7ac12a | ||
|
|
3788e8d7b3 | ||
|
|
041290e1c7 | ||
|
|
1099e786df | ||
|
|
be95169c1b | ||
|
|
a899cb48b3 | ||
|
|
ed8ac01f07 | ||
|
|
12cde3ddce | ||
|
|
860d70551f | ||
|
|
6db6fcc414 | ||
|
|
25c229de73 | ||
|
|
d954a54017 | ||
|
|
78bbf1f04f | ||
|
|
52938202bd | ||
|
|
0f29818030 | ||
|
|
dd39a25dd0 | ||
|
|
6ed4be9135 | ||
|
|
02b6a21276 | ||
|
|
794ca573b8 | ||
|
|
032f771996 | ||
|
|
3dfd7a9ab1 | ||
|
|
1713e5e2eb | ||
|
|
1d7342573b | ||
|
|
64fc295ecc | ||
|
|
6b6dc8311a | ||
|
|
b1d8c3c1e3 | ||
|
|
71037cb482 | ||
|
|
1f4e5cadd2 | ||
|
|
0de9cc82f6 | ||
|
|
7dba46df9b | ||
|
|
9cb09feb65 | ||
|
|
fb084fa4cd | ||
|
|
846b8fb57e | ||
|
|
cb649830a0 | ||
|
|
89510f40d3 | ||
|
|
9f2207eb1f | ||
|
|
dfb18b305d | ||
|
|
1606395546 | ||
|
|
8dbd79d49b | ||
|
|
e2c67fa25a | ||
|
|
cf85177c7f | ||
|
|
bc4a0f448b | ||
|
|
14ef86456f | ||
|
|
12efc85baf | ||
|
|
68124dfdbe | ||
|
|
ee4158e90b | ||
|
|
0b3b7efccf | ||
|
|
6064269936 | ||
|
|
d0929896cf | ||
|
|
990e87de1f | ||
|
|
8da47dcd00 | ||
|
|
c63d88d987 | ||
|
|
c21bde192b | ||
|
|
39f1f8aff5 | ||
|
|
8b699c58ae | ||
|
|
743a97c784 | ||
|
|
ea7ae5698a | ||
|
|
3ed9b3ec39 | ||
|
|
e8f3e3b88a | ||
|
|
82d16fbfde | ||
|
|
766dcd0dd5 | ||
|
|
87302d6d86 | ||
|
|
05b94d1d15 | ||
|
|
ed0c93aec3 | ||
|
|
03b4467173 | ||
|
|
f027cc6a3e | ||
|
|
634ce6829a | ||
|
|
64b5f9af78 | ||
|
|
9269fcb8a7 | ||
|
|
f0d29cb755 | ||
|
|
897e0f1ba6 | ||
|
|
4cfa94d304 | ||
|
|
5e2abb8a33 | ||
|
|
0dc5052e36 | ||
|
|
774ad1e2ff | ||
|
|
52e91cc309 | ||
|
|
d3e6274939 | ||
|
|
ec904eb8a4 | ||
|
|
95a09c5463 | ||
|
|
125fb598d5 | ||
|
|
f8c13f939c | ||
|
|
c846b24ebb | ||
|
|
85324d9109 | ||
|
|
10b410d46f | ||
|
|
c87fe7c265 | ||
|
|
ce0984573b | ||
|
|
b38c3a5035 | ||
|
|
15dca65bd1 | ||
|
|
e1aa43147d | ||
|
|
057b18e4be | ||
|
|
d4f2ced6a3 | ||
|
|
0b6c92fcd2 | ||
|
|
4491e71ee2 | ||
|
|
d09f78801c | ||
|
|
f26ea32897 | ||
|
|
86dbea7ddb | ||
|
|
4221ddbb35 | ||
|
|
eeb0c22d90 | ||
|
|
eb801e66ae | ||
|
|
4d0d4ca6ea | ||
|
|
56cdae67c3 | ||
|
|
b50017cad4 | ||
|
|
c85c38cde3 | ||
|
|
f606eda18d | ||
|
|
08c9e2dde3 | ||
|
|
882bf7b020 | ||
|
|
da4e70bc60 | ||
|
|
f75f720c8a | ||
|
|
8d3aff5ff1 | ||
|
|
4e8c9078f7 | ||
|
|
41e4efcf1f | ||
|
|
f55e6138a5 | ||
|
|
147e24d835 | ||
|
|
4a94858ed1 | ||
|
|
12c90bae04 | ||
|
|
6f7f10b2f7 | ||
|
|
430a2ea2f6 | ||
|
|
87569617ae | ||
|
|
b17beaccf1 | ||
|
|
cddba64151 | ||
|
|
a006bfeb24 | ||
|
|
71e1a8666d | ||
|
|
16e7872a82 | ||
|
|
aa4ebb07f8 | ||
|
|
4366d6a8c7 | ||
|
|
aa76d49234 | ||
|
|
d9df06644e | ||
|
|
554ee01263 | ||
|
|
b3055067d8 | ||
|
|
7f997d4b59 | ||
|
|
d2d959f455 | ||
|
|
a9e497f878 | ||
|
|
e423e9ffba | ||
|
|
ff99fae928 | ||
|
|
3c9983ca12 | ||
|
|
d3412e7de6 | ||
|
|
2841942899 | ||
|
|
415e79b523 | ||
|
|
9a6fdaa42b | ||
|
|
0cdbe998cb | ||
|
|
35b0c49da5 | ||
|
|
d43c9f006b | ||
|
|
01a6ae61d2 | ||
|
|
4b4b233377 | ||
|
|
ccc9907034 | ||
|
|
26520abe2b | ||
|
|
1397c3248d | ||
|
|
c40dc39b88 | ||
|
|
a2753bb27d | ||
|
|
1c5529691b | ||
|
|
8e5bdc6b2b | ||
|
|
39324d6b55 | ||
|
|
b1ad187d1b | ||
|
|
606f15fec5 | ||
|
|
09a0eb26d1 | ||
|
|
bf9e8048ff | ||
|
|
21ebc226e4 | ||
|
|
8e3fef9096 | ||
|
|
829dccc1b6 | ||
|
|
a65caa62b3 | ||
|
|
ddb6f9c5b4 | ||
|
|
e152ab6cf2 | ||
|
|
4085af023a | ||
|
|
686e401368 | ||
|
|
2fdfa64b13 | ||
|
|
f406cfcebb | ||
|
|
80f35725e6 | ||
|
|
6741d99681 | ||
|
|
ad4ec14778 | ||
|
|
2bf16ad88b | ||
|
|
6c56581c15 | ||
|
|
ce89d9fbe0 | ||
|
|
e298f74310 | ||
|
|
c550c1373e | ||
|
|
6a1bb88c3b | ||
|
|
a585e96fdf | ||
|
|
e78ef493a1 | ||
|
|
c139378694 | ||
|
|
d0eb3e760f | ||
|
|
9cfa3e5002 | ||
|
|
82278037ec | ||
|
|
0b592f86d0 | ||
|
|
ee4e003c2d | ||
|
|
b2904bc6fb | ||
|
|
67b94cf52e | ||
|
|
c308d2c9dd | ||
|
|
e36370b080 | ||
|
|
d0f005a6f1 | ||
|
|
a461fa9137 | ||
|
|
22e969fb59 | ||
|
|
34527c8395 | ||
|
|
012f4d68bc | ||
|
|
724283433a | ||
|
|
aea19b93b1 | ||
|
|
73020a711d | ||
|
|
7e62e671e3 | ||
|
|
2e845ac0ab | ||
|
|
2df41e52ed | ||
|
|
bf8e93f581 | ||
|
|
f5eec3283c | ||
|
|
223161c7d6 | ||
|
|
b8ba6e4827 | ||
|
|
15c8259ac2 | ||
|
|
720492932f | ||
|
|
a08a0fb084 | ||
|
|
827fbaac1f | ||
|
|
297224bd31 | ||
|
|
9dc835c60c | ||
|
|
67a96bc2f3 | ||
|
|
ff5bf16111 | ||
|
|
8d22059462 | ||
|
|
91d144f5f7 | ||
|
|
c894b1d335 | ||
|
|
d30a7bf6a1 | ||
|
|
649d5f56c9 | ||
|
|
36b033c2f7 | ||
|
|
dc9bce915b | ||
|
|
c9a7cfafd0 | ||
|
|
9d9585ecba | ||
|
|
0ebd7e6a58 | ||
|
|
a100abc3b6 | ||
|
|
94f6945d5a | ||
|
|
442d771a7a | ||
|
|
242e11eefc | ||
|
|
7fbbae8cd4 | ||
|
|
b80d96d9c7 | ||
|
|
d8e69360b9 | ||
|
|
83ae9b66a3 | ||
|
|
4ac052c10b | ||
|
|
f1717f8319 | ||
|
|
cb000549bd | ||
|
|
2712aa2ae2 | ||
|
|
7dcefa6bee | ||
|
|
70e3528809 | ||
|
|
c7780a2708 | ||
|
|
26bab84d04 | ||
|
|
efea6136e7 | ||
|
|
c56026c18a | ||
|
|
1d9504d0f0 | ||
|
|
780b1f8acc | ||
|
|
9b6c5741de | ||
|
|
0742e18edd | ||
|
|
cb59c2489b | ||
|
|
3158b544d5 | ||
|
|
e80270d1fa | ||
|
|
99b37f24bb | ||
|
|
fdbdcc4130 | ||
|
|
08104966b2 | ||
|
|
1c99133177 | ||
|
|
28780dc67e | ||
|
|
423ce54d8a | ||
|
|
73bab73db6 | ||
|
|
3e90471fa2 | ||
|
|
7f2db8a9a7 | ||
|
|
d5fc147ffd | ||
|
|
77ffd7bbf9 | ||
|
|
42775d3477 | ||
|
|
6932b8c378 | ||
|
|
cff641ef80 | ||
|
|
373abf1b24 | ||
|
|
f668596667 | ||
|
|
93e0e815d7 | ||
|
|
05275c8938 | ||
|
|
19475db7dd | ||
|
|
aa315d7c97 | ||
|
|
080b25e30c | ||
|
|
a7c8c08183 | ||
|
|
bb98627d2b | ||
|
|
3060866586 | ||
|
|
eacf121f78 | ||
|
|
c39535fdd0 | ||
|
|
c3311df2a8 | ||
|
|
5a17314b2c | ||
|
|
07281f050c | ||
|
|
9c3757fb0c | ||
|
|
f5d5e59040 | ||
|
|
70880c066f | ||
|
|
5508c70f3a |
@@ -15,5 +15,4 @@ trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
test/assets
|
||||
test/reports
|
||||
test/sample
|
||||
test/tmp
|
||||
packages/bower-logger/test
|
||||
@@ -17,9 +17,8 @@
|
||||
"strict": 0,
|
||||
"semi": 0,
|
||||
"comma-spacing": 2,
|
||||
"quote-props": [2, "consistent", { "keywords": true }],
|
||||
"quote-props": [2, "as-needed"],
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"indent": [2, 4],
|
||||
"no-cond-assign": [ 2, "except-parens" ],
|
||||
"no-debugger": 2,
|
||||
"no-dupe-args": 2,
|
||||
@@ -41,7 +40,7 @@
|
||||
"no-new-wrappers": 2,
|
||||
"no-invalid-this": 0,
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"keyword-spacing": 2,
|
||||
"new-parens": 2,
|
||||
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: bower
|
||||
44
.github/ISSUE_TEMPLATE.md
vendored
Normal file
44
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<!--
|
||||
|
||||
If you are reporting a new issue, make sure that we do not have any duplicates.
|
||||
You can ensure this by searching the issue list for this repository.
|
||||
|
||||
You are welcome to open issues to discuss important general topics concerning Bower.
|
||||
However for support questions, please consider using http://stackoverflow.com or
|
||||
asking for help in our Discord channel: https://discordapp.com/invite/0fFM7QF0KpZaDeN9
|
||||
|
||||
# BUG REPORT
|
||||
|
||||
Use the commands below to provide key information to reproduce:
|
||||
You do NOT have to include this information if this is a FEATURE REQUEST OR DISCUSSION
|
||||
|
||||
For more information about reporting bugs, see:
|
||||
https://github.com/bower/bower/wiki/Report-a-Bug
|
||||
|
||||
-->
|
||||
|
||||
**Output of `bower -v && npm -v && node -v`:**
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
**Additional environment details (proxy, private registry, etc.):**
|
||||
|
||||
|
||||
|
||||
**Steps to reproduce the issue:**
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Describe the results you received:**
|
||||
|
||||
|
||||
|
||||
**Describe the results you expected:**
|
||||
|
||||
|
||||
|
||||
**Additional information:**
|
||||
62
.github/workflows/nodejs.yml
vendored
Normal file
62
.github/workflows/nodejs.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Node v${{ matrix.node-version }} on ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# https://github.com/actions/setup-node/issues/27
|
||||
node-version: [0.10.x, 0.12.x, 4.x, 6.x, 8.x, 10.x, 12.x, 14.x]
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Set git config
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.symlinks true
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: install
|
||||
run: yarn
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: lint
|
||||
run: npm run lint
|
||||
- name: bower tests
|
||||
run: npm test
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-logger tests
|
||||
run: (cd packages/bower-logger && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-config tests
|
||||
run: (cd packages/bower-config && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-endpoint-parser tests
|
||||
run: (cd packages/bower-endpoint-parser && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-json tests
|
||||
run: (cd packages/bower-json && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-registry-client tests
|
||||
run: (cd packages/bower-registry-client && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
!lib/bin
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
|
||||
|
||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
||||
**/node_modules/**
|
||||
**/test/assets/**
|
||||
**/bower_components/**
|
||||
test/sample
|
||||
31
.travis.yml
31
.travis.yml
@@ -1,31 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
- NODE_VERSION=0.10
|
||||
- NODE_VERSION=0.11
|
||||
- NODE_VERSION=0.12
|
||||
- NODE_VERSION=4.0
|
||||
- NODE_VERSION=5.0
|
||||
|
||||
install:
|
||||
- test $TRAVIS_OS_NAME = "osx" && brew install nvm && source $(brew --prefix nvm)/nvm.sh || test $TRAVIS_OS_NAME = "linux"
|
||||
- nvm install $NODE_VERSION
|
||||
- npm install -g npm@^2.0.0
|
||||
- node --version
|
||||
- npm --version
|
||||
- git --version
|
||||
- svn --version | head -n 1
|
||||
- npm install -g grunt-cli
|
||||
- npm install
|
||||
|
||||
os:
|
||||
- osx
|
||||
- linux
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- os: osx
|
||||
- env: "NODE_VERSION=0.11"
|
||||
|
||||
script:
|
||||
- grunt travis
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,6 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## 1.7.8 - 2016-01-04
|
||||
## Newer releases
|
||||
|
||||
Please see: https://github.com/bower/bower/releases
|
||||
|
||||
## 1.8.0 - 2016-11-07
|
||||
|
||||
- Download tar archives from GitHub when possible (#2263)
|
||||
- Change default shorthand resolver for github from `git://` to `https://`
|
||||
- Fix ssl handling by not setting GIT_SSL_NO_VERIFY=false (#2361)
|
||||
- Allow for removing components with url instead of name (#2368)
|
||||
- Show in warning message location of malformed bower.json (#2357)
|
||||
- Improve handling of non-semver versions in git resolver (#2316)
|
||||
- Fix handling of cached releases pluginResolverFactory (#2356)
|
||||
- Allow to type the entire version when conflict occured (#2243)
|
||||
- Allow `owner/reponame` shorthand for registering components (#2248)
|
||||
- Allow single-char repo names and package names (#2249)
|
||||
- Make `bower version` no longer honor `version` in bower.json (#2232)
|
||||
- Add `postinstall` hook (#2252)
|
||||
- Allow for `@` instead of `#` for `install` and `info` commands (#2322)
|
||||
- Upgrade all bundled modules
|
||||
|
||||
## 1.7.9 - 2016-04-05
|
||||
|
||||
- Show warnings for invalid bower.json fields
|
||||
- Update bower-json
|
||||
- Less strict validation on package name (allow spaces, slashes, and "@")
|
||||
|
||||
## 1.7.8 - 2016-04-04
|
||||
|
||||
- Don't ask for git credentials in non-interactive session, fixes #956 #1009
|
||||
- Prevent swallowing exceptions with programmatic api, fixes #2187
|
||||
@@ -11,6 +38,8 @@
|
||||
- Update opn package to fix issues with "bower open" command on Windows
|
||||
- Update bower-config
|
||||
- Do not interpolate environment variables in script hooks, fixes bower/config#47
|
||||
- Update bower-json
|
||||
- Validate package name more strictly and allow only latin letters, dots, dashes and underscores
|
||||
- Add support for "save" and "save-exact" in .bowerrc, #2161
|
||||
|
||||
## 1.7.7 - 2016-01-27
|
||||
@@ -75,7 +104,7 @@ https://github.com/npm/npm/issues/11227
|
||||
- Update bower config
|
||||
- Loads the .bowerrc file from the cwd specified on the command line
|
||||
- Allow the use of environment variables in .bowerrc ([#41](https://github.com/bower/config/issues/41))
|
||||
- Allow for array notation in ENV variables ([#44](https://github.com/bower/config/issues/44))
|
||||
- Allow for array notation in ENV variables ([#44](https://github.com/bower/config/issues/44))
|
||||
|
||||
## 1.6.9 - 2015-12-04
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Bower is a large community project with many different developers contributing a
|
||||
|
||||
## Team Meetings
|
||||
|
||||
We communicate through a channel on slack: https://gitter.im/bower
|
||||
We communicate through a channel on Discord https://discord.gg/0fFM7QF0KpZRh2cY
|
||||
|
||||
If you'd like to attend the meetings, please fill the [support form](http://goo.gl/forms/P1ndzCNoiG), and you'll get an invite.
|
||||
|
||||
@@ -29,9 +29,9 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests. Use
|
||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower)
|
||||
[Gitter Channel](https://gitter.im/bower/bower)
|
||||
[Mailing List](http://groups.google.com/group/twitter-bower)
|
||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower),
|
||||
[Discord Channel](https://discordapp.com/channels/119103197720739842/123728452816732160),
|
||||
[Mailing List](http://groups.google.com/group/twitter-bower),
|
||||
(twitter-bower@googlegroups.com), or
|
||||
[#bower](http://webchat.freenode.net/?channels=bower) on Freenode.
|
||||
|
||||
|
||||
213
Gruntfile.js
213
Gruntfile.js
@@ -1,213 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var tmp = require('tmp');
|
||||
var childProcess = require('child_process');
|
||||
var arraydiff = require('arr-diff');
|
||||
var fs = require('fs');
|
||||
var wrench = require('wrench');
|
||||
var inquirer = require('inquirer');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.initConfig({
|
||||
eslint: {
|
||||
options: {
|
||||
fix: true
|
||||
},
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'bin/*',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/assets/**/*',
|
||||
'!test/reports/**/*',
|
||||
'!test/sample/**/*',
|
||||
'!test/tmp/**/*'
|
||||
]
|
||||
},
|
||||
simplemocha: {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
timeout: '15000'
|
||||
},
|
||||
full: {
|
||||
src: ['test/test.js']
|
||||
},
|
||||
short: {
|
||||
options: {
|
||||
reporter: 'dot'
|
||||
},
|
||||
src: ['test/test.js']
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
'assets': {
|
||||
command: 'node test/packages.js && node test/packages-svn.js'
|
||||
},
|
||||
'assets-force': {
|
||||
command: 'node test/packages.js --force && node test/packages-svn.js --force'
|
||||
},
|
||||
'cover': {
|
||||
command: 'node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- --timeout 30000 -R dot test/test.js'
|
||||
},
|
||||
'coveralls': {
|
||||
command: 'npm run coveralls < test/reports/lcov.info',
|
||||
exitCodes: [0, 1, 2, 3] // Alow for failure for coverage report
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['<%= eslint.files %>'],
|
||||
tasks: ['eslint', 'simplemocha:short']
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('assets', ['exec:assets-force']);
|
||||
grunt.registerTask('test', ['eslint', 'exec:assets', 'simplemocha:full']);
|
||||
grunt.registerTask('cover', 'exec:cover');
|
||||
grunt.registerTask('travis', ['eslint', 'exec:assets', 'exec:cover', 'exec:coveralls']);
|
||||
grunt.registerTask('default', 'test');
|
||||
|
||||
grunt.task.registerTask('publish', 'Perform final checks and publish Bower', function () {
|
||||
var npmVersion = JSON.parse(childProcess.execSync('npm version --json').toString()).npm.split('.');
|
||||
var npmMajor = parseInt(npmVersion[0], 10);
|
||||
var npmMinor = parseInt(npmVersion[1], 10);
|
||||
|
||||
var jsonPackage = require('./package');
|
||||
|
||||
if (npmMajor !== 3 || npmMinor < 5) {
|
||||
grunt.log.writeln('You need to use at least npm@3.5 to publish bower.');
|
||||
grunt.log.writeln('It is because npm 2.x produces too long paths that Windows does not handle.');
|
||||
grunt.log.writeln('Please upgrade it: npm install -g npm');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var version = jsonPackage.version;
|
||||
var changelog = fs.readFileSync('./CHANGELOG.md');
|
||||
|
||||
if (changelog.indexOf('## ' + version) === -1) {
|
||||
grunt.log.writeln('Please add changelog.md entry for this bower version (' + version + ')');
|
||||
|
||||
var lastRelease = childProcess.execSync('git tag | tail -1').toString().trim();
|
||||
|
||||
grunt.log.writeln('Commits since last release (' + lastRelease + '): \n');
|
||||
|
||||
grunt.log.writeln(childProcess.execSync('git log --oneline ' + lastRelease + '..').toString());
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim() !== 'master') {
|
||||
grunt.log.writeln('You need to release bower from the "master" branch');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.env.SKIP_TESTS !== '1') {
|
||||
grunt.log.writeln('Reinstalling dependencies...');
|
||||
childProcess.execSync('rm -rf node_modules && npm install', { stdio: [0, 1, 2] });
|
||||
|
||||
grunt.log.writeln('Running test suite...');
|
||||
childProcess.execSync('grunt test', { stdio: [0, 1, 2] });
|
||||
}
|
||||
|
||||
var dir = tmp.dirSync().name;
|
||||
|
||||
wrench.copyDirSyncRecursive(__dirname, dir, {
|
||||
forceDelete: true,
|
||||
include: function (path) {
|
||||
return !path.match(/node_modules|\.git|test/);
|
||||
}
|
||||
});
|
||||
|
||||
grunt.log.writeln('Installing production dependencies...');
|
||||
childProcess.execSync('npm install --production --silent', { cwd: dir, stdio: [0, 1, 2] });
|
||||
|
||||
delete jsonPackage.dependencies;
|
||||
delete jsonPackage.devDependencies;
|
||||
delete jsonPackage.scripts;
|
||||
|
||||
fs.writeFileSync(path.resolve(dir, 'package.json'), JSON.stringify(jsonPackage, null, ' ') + '\n');
|
||||
|
||||
|
||||
grunt.log.writeln('Moving node_modules to lib directory...');
|
||||
|
||||
wrench.copyDirSyncRecursive(path.resolve(dir, 'node_modules'), path.resolve(dir, 'lib', 'node_modules'));
|
||||
wrench.rmdirSyncRecursive(path.resolve(dir, 'node_modules'));
|
||||
|
||||
grunt.log.writeln('Testing bower on sample project...');
|
||||
|
||||
childProcess.execSync(
|
||||
'cd test/sample && rm -rf bower_components && ' + dir + '/bin/bower install --force', { stdio: [0, 1, 2] }
|
||||
);
|
||||
|
||||
var expectedPackages = (
|
||||
'SHA-1 ace-builds almond angular angular-animate angular-bootstrap angular-charts angular-contenteditable ' +
|
||||
'angular-deckgrid angular-fullscreen angular-gravatar angular-hotkeys angular-local-storage angular-marked ' +
|
||||
'angular-moment angular-sanitize angular-touch angular-ui-router angular-ui-sortable ' +
|
||||
'angulartics asEvented bootstrap coffee-script d3 es6-shim font-awesome howler jquery ' +
|
||||
'jquery-ui jquery-waypoints js-beautify lodash lz-string marked moment ng-file-upload peerjs ' +
|
||||
'requirejs restangular slimScroll slimScrollHorizontal venturocket-angular-slider'
|
||||
).split(' ');
|
||||
|
||||
var installedPackages = fs.readdirSync('./test/sample/bower_components');
|
||||
|
||||
var installedDiff = arraydiff(expectedPackages, installedPackages);
|
||||
|
||||
if (installedDiff.length > 0) {
|
||||
grunt.log.writeln('ERROR. Some packages were not installed by bower: ');
|
||||
grunt.log.writeln(installedDiff.join(', '));
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
grunt.log.writeln('\nBower production bundle installed in:');
|
||||
grunt.log.writeln(dir + '\n');
|
||||
|
||||
var questions = [
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'review',
|
||||
message: 'Did you review all the changes with "git diff"?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'changelog',
|
||||
message: 'Are you sure the CHANGELOG.md contains all changes?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'tests',
|
||||
message: 'Are you sure all tests are passing on Travis and Appveyor?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'publish',
|
||||
message: 'Are you SURE you want to publish ' + jsonPackage.name + '@' + jsonPackage.version + '?',
|
||||
default: false
|
||||
}
|
||||
];
|
||||
|
||||
var done = this.async();
|
||||
|
||||
inquirer.prompt(questions, function (answers) {
|
||||
if (!answers.review || !answers.changelog || !answers.tests || !answers.publish) {
|
||||
grunt.log.writeln('Please publish bower after you fix this issue');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
grunt.log.writeln('\nPlease remember to tag this relese, and add a release on Github!');
|
||||
grunt.log.writeln('\nAlso, please remember to test published Bower one more time!');
|
||||
grunt.log.writeln('\nPublishing Bower...');
|
||||
|
||||
childProcess.execSync('npm publish --tag beta', { cwd: dir, stdio: [0, 1, 2] });
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Twitter and other contributors
|
||||
Copyright (c) 2013-present Twitter and other contributors
|
||||
|
||||
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
|
||||
|
||||
121
README.md
121
README.md
@@ -1,13 +1,10 @@
|
||||
# Bower - A package manager for the web
|
||||
|
||||
> Bower needs resources for its maintenance. Please fill [this form](https://docs.google.com/forms/d/1i-Opb-uPdqUBBZQSbngv3Y3bfolG1gbBvtRLfxMnzRE/viewform?c=0&w=1) if you think you can help.
|
||||
[](https://github.com/bower/bower/actions?query=branch%3Amaster)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
|
||||
[](https://travis-ci.org/bower/bower)
|
||||
[](https://ci.appveyor.com/project/sheerun/bower/history)
|
||||
[](https://coveralls.io/r/bower/bower?branch=master)
|
||||
[](https://discord.gg/0fFM7QF0KpZRh2cY)
|
||||
[](http://issuestats.com/github/bower/bower)
|
||||
[](http://issuestats.com/github/bower/bower)
|
||||
> ..psst! While Bower is maintained, we recommend [yarn](https://yarnpkg.com/) and [webpack](https://webpack.js.org/) or [parcel](https://parceljs.org/) for new front-end projects!
|
||||
|
||||
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
|
||||
|
||||
@@ -119,8 +116,116 @@ Note that on Windows for tests to pass you need to configure Git before cloning:
|
||||
git config --global core.autocrlf input
|
||||
```
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/bower#backer)]
|
||||
|
||||
<a href="https://opencollective.com/bower/backer/0/website" target="_blank"><img src="https://opencollective.com/bower/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/1/website" target="_blank"><img src="https://opencollective.com/bower/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/2/website" target="_blank"><img src="https://opencollective.com/bower/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/3/website" target="_blank"><img src="https://opencollective.com/bower/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/4/website" target="_blank"><img src="https://opencollective.com/bower/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/5/website" target="_blank"><img src="https://opencollective.com/bower/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/6/website" target="_blank"><img src="https://opencollective.com/bower/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/7/website" target="_blank"><img src="https://opencollective.com/bower/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/8/website" target="_blank"><img src="https://opencollective.com/bower/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/9/website" target="_blank"><img src="https://opencollective.com/bower/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/10/website" target="_blank"><img src="https://opencollective.com/bower/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/11/website" target="_blank"><img src="https://opencollective.com/bower/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/12/website" target="_blank"><img src="https://opencollective.com/bower/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/13/website" target="_blank"><img src="https://opencollective.com/bower/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/14/website" target="_blank"><img src="https://opencollective.com/bower/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/15/website" target="_blank"><img src="https://opencollective.com/bower/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/16/website" target="_blank"><img src="https://opencollective.com/bower/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/17/website" target="_blank"><img src="https://opencollective.com/bower/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/18/website" target="_blank"><img src="https://opencollective.com/bower/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/19/website" target="_blank"><img src="https://opencollective.com/bower/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/20/website" target="_blank"><img src="https://opencollective.com/bower/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/21/website" target="_blank"><img src="https://opencollective.com/bower/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/22/website" target="_blank"><img src="https://opencollective.com/bower/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/23/website" target="_blank"><img src="https://opencollective.com/bower/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/24/website" target="_blank"><img src="https://opencollective.com/bower/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/25/website" target="_blank"><img src="https://opencollective.com/bower/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/26/website" target="_blank"><img src="https://opencollective.com/bower/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/27/website" target="_blank"><img src="https://opencollective.com/bower/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/28/website" target="_blank"><img src="https://opencollective.com/bower/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/29/website" target="_blank"><img src="https://opencollective.com/bower/backer/29/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/30/website" target="_blank"><img src="https://opencollective.com/bower/backer/30/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/31/website" target="_blank"><img src="https://opencollective.com/bower/backer/31/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/32/website" target="_blank"><img src="https://opencollective.com/bower/backer/32/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/33/website" target="_blank"><img src="https://opencollective.com/bower/backer/33/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/34/website" target="_blank"><img src="https://opencollective.com/bower/backer/34/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/35/website" target="_blank"><img src="https://opencollective.com/bower/backer/35/avatar.svg"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/bower#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/bower/sponsor/0/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/1/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/2/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/3/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/4/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/5/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/6/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/7/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/8/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/9/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/10/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/11/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/12/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/13/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/14/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/15/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/16/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/17/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/18/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/19/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/20/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/21/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/22/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/23/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/24/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/25/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/26/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/27/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/28/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/29/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/29/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/30/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/30/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/31/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/31/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/32/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/32/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/33/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/33/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/34/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/34/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/35/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/35/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/36/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/36/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/37/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/37/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/38/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/38/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/39/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/39/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/40/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/40/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/41/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/41/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/42/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/42/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/43/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/43/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/44/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/44/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/45/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/45/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/46/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/46/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/47/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/47/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/48/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/48/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/49/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/49/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/50/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/50/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/51/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/51/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/52/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/52/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/53/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/53/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/54/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/54/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/55/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/55/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/56/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/56/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/57/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/57/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/58/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/58/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/59/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/59/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/60/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/60/avatar.svg"></a>
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2016 Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
|
||||
Copyright (c) 2012-present Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
|
||||
|
||||
Licensed under the MIT License
|
||||
|
||||
43
appveyor.yml
43
appveyor.yml
@@ -1,43 +0,0 @@
|
||||
# Thanks for Grunt for template of this file!
|
||||
|
||||
# http://www.appveyor.com/docs/appveyor-yml
|
||||
|
||||
# Fix line endings in Windows. (runs before repo cloning)
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.12"
|
||||
- nodejs_version: "5"
|
||||
- nodejs_version: "4"
|
||||
|
||||
# Allow failing jobs for bleeding-edge Node.js versions.
|
||||
matrix:
|
||||
allow_failures:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "5"
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node 0.STABLE.latest
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
# Output useful info for debugging.
|
||||
- node --version
|
||||
- npm --version
|
||||
- git --version
|
||||
- svn --version
|
||||
# Install all dependencies
|
||||
- npm install
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
- cmd: npm run ci
|
||||
|
||||
# Don't actually build.
|
||||
build: off
|
||||
|
||||
# Set build version format here instead of in the admin panel.
|
||||
version: "{build}"
|
||||
@@ -18,8 +18,8 @@ var logger;
|
||||
var levels = Logger.LEVELS;
|
||||
|
||||
options = cli.readOptions({
|
||||
'version': { type: Boolean, shorthand: 'v' },
|
||||
'help': { type: Boolean, shorthand: 'h' },
|
||||
version: { type: Boolean, shorthand: 'v' },
|
||||
help: { type: Boolean, shorthand: 'h' },
|
||||
'allow-root': { type: Boolean }
|
||||
});
|
||||
|
||||
@@ -73,12 +73,12 @@ command = command && command.replace(/\./g, ' ');
|
||||
if (!commandFunc) {
|
||||
logger = bower.commands.help();
|
||||
command = 'help';
|
||||
// If the user requested help, show the command's help
|
||||
// Do the same if the actual command is a group of other commands (e.g.: cache)
|
||||
// If the user requested help, show the command's help
|
||||
// Do the same if the actual command is a group of other commands (e.g.: cache)
|
||||
} else if (options.help || !commandFunc.line) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
// Call the line method
|
||||
// Call the line method
|
||||
} else {
|
||||
logger = commandFunc.line(process.argv);
|
||||
|
||||
@@ -95,42 +95,47 @@ renderer = cli.getRenderer(command, logger.json, bower.config);
|
||||
|
||||
function handleLogger(logger, renderer) {
|
||||
logger
|
||||
.on('end', function (data) {
|
||||
if (!bower.config.silent && !bower.config.quiet) {
|
||||
renderer.end(data);
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
if (command !== 'help' && err.code === 'EREADOPTIONS') {
|
||||
logger = bower.commands.help(command);
|
||||
renderer = cli.getRenderer('help', logger.json, bower.config);
|
||||
handleLogger(logger, renderer);
|
||||
} else {
|
||||
if (levels.error >= loglevel) {
|
||||
renderer.error(err);
|
||||
.on('end', function(data) {
|
||||
if (!bower.config.silent && !bower.config.quiet) {
|
||||
renderer.end(data);
|
||||
}
|
||||
})
|
||||
.on('error', function(err) {
|
||||
if (
|
||||
command !== 'help' &&
|
||||
(err.code === 'EREADOPTIONS' || err.code === 'EINVFORMAT')
|
||||
) {
|
||||
logger = bower.commands.help(command);
|
||||
renderer = cli.getRenderer('help', logger.json, bower.config);
|
||||
handleLogger(logger, renderer);
|
||||
} else {
|
||||
if (levels.error >= loglevel) {
|
||||
renderer.error(err);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.on('log', function (log) {
|
||||
if (levels[log.level] >= loglevel) {
|
||||
renderer.log(log);
|
||||
}
|
||||
})
|
||||
.on('prompt', function (prompt, callback) {
|
||||
renderer.prompt(prompt)
|
||||
.then(function (answer) {
|
||||
callback(answer);
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.on('log', function(log) {
|
||||
if (levels[log.level] >= loglevel) {
|
||||
renderer.log(log);
|
||||
}
|
||||
})
|
||||
.on('prompt', function(prompt, callback) {
|
||||
renderer.prompt(prompt).then(function(answer) {
|
||||
callback(answer);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleLogger(logger, renderer);
|
||||
|
||||
// Warn if HOME is not SET
|
||||
if (!userHome) {
|
||||
logger.warn('no-home', 'HOME environment variable not set. User config will not be loaded.');
|
||||
logger.warn(
|
||||
'no-home',
|
||||
'HOME environment variable not set. User config will not be loaded.'
|
||||
);
|
||||
}
|
||||
|
||||
if (bower.config.interactive) {
|
||||
|
||||
128
lib/commands/cache/clean.js
vendored
128
lib/commands/cache/clean.js
vendored
@@ -22,10 +22,10 @@ function clean(logger, endpoints, options, config) {
|
||||
|
||||
// Generate decomposed endpoints and names based on the endpoints
|
||||
if (endpoints) {
|
||||
decEndpoints = endpoints.map(function (endpoint) {
|
||||
decEndpoints = endpoints.map(function(endpoint) {
|
||||
return endpointParser.decompose(endpoint);
|
||||
});
|
||||
names = decEndpoints.map(function (decEndpoint) {
|
||||
names = decEndpoints.map(function(decEndpoint) {
|
||||
return decEndpoint.name || decEndpoint.source;
|
||||
});
|
||||
}
|
||||
@@ -33,27 +33,26 @@ function clean(logger, endpoints, options, config) {
|
||||
return Q.all([
|
||||
clearPackages(decEndpoints, config, logger),
|
||||
clearLinks(names, config, logger)
|
||||
])
|
||||
.spread(function (entries) {
|
||||
]).spread(function(entries) {
|
||||
return entries;
|
||||
});
|
||||
}
|
||||
|
||||
function clearPackages(decEndpoints, config, logger) {
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var repository = new PackageRepository(config, logger);
|
||||
|
||||
return repository.list()
|
||||
.then(function (entries) {
|
||||
return repository.list().then(function(entries) {
|
||||
var promises;
|
||||
|
||||
// Filter entries according to the specified packages
|
||||
if (decEndpoints) {
|
||||
entries = entries.filter(function (entry) {
|
||||
return !!mout.array.find(decEndpoints, function (decEndpoint) {
|
||||
entries = entries.filter(function(entry) {
|
||||
return !!mout.array.find(decEndpoints, function(decEndpoint) {
|
||||
var entryPkgMeta = entry.pkgMeta;
|
||||
|
||||
// Check if name or source match the entry
|
||||
if (decEndpoint.name !== entryPkgMeta.name &&
|
||||
if (
|
||||
decEndpoint.name !== entryPkgMeta.name &&
|
||||
decEndpoint.source !== entryPkgMeta.name &&
|
||||
decEndpoint.source !== entryPkgMeta._source
|
||||
) {
|
||||
@@ -67,36 +66,47 @@ function clearPackages(decEndpoints, config, logger) {
|
||||
|
||||
// If it's a semver target, compare using semver spec
|
||||
if (semver.validRange(decEndpoint.target)) {
|
||||
return semver.satisfies(entryPkgMeta.version, decEndpoint.target);
|
||||
return semver.satisfies(
|
||||
entryPkgMeta.version,
|
||||
decEndpoint.target
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, compare against target/release
|
||||
return decEndpoint.target === entryPkgMeta._target ||
|
||||
decEndpoint.target === entryPkgMeta._release;
|
||||
return (
|
||||
decEndpoint.target === entryPkgMeta._target ||
|
||||
decEndpoint.target === entryPkgMeta._release
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
promises = entries.map(function (entry) {
|
||||
return repository.eliminate(entry.pkgMeta)
|
||||
.then(function () {
|
||||
logger.info('deleted', 'Cached package ' + entry.pkgMeta.name + ': ' + entry.canonicalDir, {
|
||||
file: entry.canonicalDir
|
||||
});
|
||||
promises = entries.map(function(entry) {
|
||||
return repository.eliminate(entry.pkgMeta).then(function() {
|
||||
logger.info(
|
||||
'deleted',
|
||||
'Cached package ' +
|
||||
entry.pkgMeta.name +
|
||||
': ' +
|
||||
entry.canonicalDir,
|
||||
{
|
||||
file: entry.canonicalDir
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return Q.all(promises)
|
||||
.then(function () {
|
||||
if (!decEndpoints) {
|
||||
// Ensure that everything is cleaned,
|
||||
// even invalid packages in the cache
|
||||
return repository.clear();
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return entries;
|
||||
});
|
||||
.then(function() {
|
||||
if (!decEndpoints) {
|
||||
// Ensure that everything is cleaned,
|
||||
// even invalid packages in the cache
|
||||
return repository.clear();
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
return entries;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,59 +116,59 @@ function clearLinks(names, config, logger) {
|
||||
|
||||
// If no names are passed, grab all links
|
||||
if (!names) {
|
||||
promise = Q.nfcall(fs.readdir, dir)
|
||||
.fail(function (err) {
|
||||
promise = Q.nfcall(fs.readdir, dir).fail(function(err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
// Otherwise use passed ones
|
||||
// Otherwise use passed ones
|
||||
} else {
|
||||
promise = Q.resolve(names);
|
||||
}
|
||||
|
||||
return promise
|
||||
.then(function (names) {
|
||||
return promise.then(function(names) {
|
||||
var promises;
|
||||
var linksToRemove = [];
|
||||
|
||||
// Decide which links to delete
|
||||
promises = names.map(function (name) {
|
||||
promises = names.map(function(name) {
|
||||
var link = path.join(config.storage.links, name);
|
||||
|
||||
return Q.nfcall(fs.readlink, link)
|
||||
.then(function (linkTarget) {
|
||||
// Link exists, check if it points to a folder
|
||||
// that still exists
|
||||
return Q.nfcall(fs.stat, linkTarget)
|
||||
.then(function (stat) {
|
||||
// Target is not a folder..
|
||||
if (!stat.isDirectory()) {
|
||||
return Q.nfcall(fs.readlink, link).then(
|
||||
function(linkTarget) {
|
||||
// Link exists, check if it points to a folder
|
||||
// that still exists
|
||||
return (
|
||||
Q.nfcall(fs.stat, linkTarget)
|
||||
.then(function(stat) {
|
||||
// Target is not a folder..
|
||||
if (!stat.isDirectory()) {
|
||||
linksToRemove.push(link);
|
||||
}
|
||||
})
|
||||
// Error occurred reading the link
|
||||
.fail(function() {
|
||||
linksToRemove.push(link);
|
||||
})
|
||||
);
|
||||
// Ignore if link does not exist
|
||||
},
|
||||
function(err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
linksToRemove.push(link);
|
||||
}
|
||||
})
|
||||
// Error occurred reading the link
|
||||
.fail(function () {
|
||||
linksToRemove.push(link);
|
||||
});
|
||||
// Ignore if link does not exist
|
||||
}, function (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
linksToRemove.push(link);
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
return Q.all(promises)
|
||||
.then(function () {
|
||||
return Q.all(promises).then(function() {
|
||||
var promises;
|
||||
|
||||
// Remove each link that was declared as invalid
|
||||
promises = linksToRemove.map(function (link) {
|
||||
return Q.nfcall(rimraf, link)
|
||||
.then(function () {
|
||||
promises = linksToRemove.map(function(link) {
|
||||
return Q.nfcall(rimraf, link).then(function() {
|
||||
logger.info('deleted', 'Invalid link: ' + link, {
|
||||
file: link
|
||||
});
|
||||
@@ -172,7 +182,7 @@ function clearLinks(names, config, logger) {
|
||||
|
||||
// -------------------
|
||||
|
||||
clean.readOptions = function (argv) {
|
||||
clean.readOptions = function(argv) {
|
||||
var cli = require('../../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var endpoints = options.argv.remain.slice(2);
|
||||
|
||||
9
lib/commands/cache/list.js
vendored
9
lib/commands/cache/list.js
vendored
@@ -13,12 +13,11 @@ function list(logger, packages, options, config) {
|
||||
packages = null;
|
||||
}
|
||||
|
||||
return repository.list()
|
||||
.then(function (entries) {
|
||||
return repository.list().then(function(entries) {
|
||||
if (packages) {
|
||||
// Filter entries according to the specified packages
|
||||
entries = entries.filter(function (entry) {
|
||||
return !!mout.array.find(packages, function (pkg) {
|
||||
entries = entries.filter(function(entry) {
|
||||
return !!mout.array.find(packages, function(pkg) {
|
||||
return pkg === entry.pkgMeta.name;
|
||||
});
|
||||
});
|
||||
@@ -30,7 +29,7 @@ function list(logger, packages, options, config) {
|
||||
|
||||
// -------------------
|
||||
|
||||
list.readOptions = function (argv) {
|
||||
list.readOptions = function(argv) {
|
||||
var cli = require('../../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var packages = options.argv.remain.slice(2);
|
||||
|
||||
@@ -7,15 +7,17 @@ function help(logger, name, config) {
|
||||
var json;
|
||||
|
||||
if (name) {
|
||||
json = path.resolve(__dirname, '../templates/json/help-' + name.replace(/\s+/g, '/') + '.json');
|
||||
json = path.resolve(
|
||||
__dirname,
|
||||
'../templates/json/help-' + name.replace(/\s+/g, '/') + '.json'
|
||||
);
|
||||
} else {
|
||||
json = path.resolve(__dirname, '../templates/json/help.json');
|
||||
}
|
||||
|
||||
return Q.promise(function (resolve) {
|
||||
return Q.promise(function(resolve) {
|
||||
fs.exists(json, resolve);
|
||||
})
|
||||
.then(function (exists) {
|
||||
}).then(function(exists) {
|
||||
if (!exists) {
|
||||
throw createError('Unknown command: ' + name, 'EUNKNOWNCMD', {
|
||||
command: name
|
||||
@@ -28,7 +30,7 @@ function help(logger, name, config) {
|
||||
|
||||
// -------------------
|
||||
|
||||
help.readOptions = function (argv) {
|
||||
help.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain.slice(1).join(' ');
|
||||
|
||||
@@ -16,8 +16,7 @@ function home(logger, name, config) {
|
||||
// If no name is specified, read the project json
|
||||
// If a name is specified, fetch from the package repository
|
||||
if (!name) {
|
||||
promise = project.hasJson()
|
||||
.then(function (json) {
|
||||
promise = project.hasJson().then(function(json) {
|
||||
if (!json) {
|
||||
throw createError('You are not inside a package', 'ENOENT');
|
||||
}
|
||||
@@ -26,14 +25,16 @@ function home(logger, name, config) {
|
||||
});
|
||||
} else {
|
||||
decEndpoint = endpointParser.decompose(name);
|
||||
promise = project.getPackageRepository().fetch(decEndpoint)
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
return pkgMeta;
|
||||
});
|
||||
promise = project
|
||||
.getPackageRepository()
|
||||
.fetch(decEndpoint)
|
||||
.spread(function(canonicalDir, pkgMeta) {
|
||||
return pkgMeta;
|
||||
});
|
||||
}
|
||||
|
||||
// Get homepage and open it
|
||||
return promise.then(function (pkgMeta) {
|
||||
return promise.then(function(pkgMeta) {
|
||||
var homepage = pkgMeta.homepage;
|
||||
|
||||
if (!homepage) {
|
||||
@@ -47,7 +48,7 @@ function home(logger, name, config) {
|
||||
|
||||
// -------------------
|
||||
|
||||
home.readOptions = function (argv) {
|
||||
home.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
@@ -14,7 +14,7 @@ function commandFactory(id) {
|
||||
var command = require(id);
|
||||
var commandArgs = [].slice.call(arguments);
|
||||
|
||||
return withLogger(function (logger) {
|
||||
return withLogger(function(logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
@@ -27,7 +27,7 @@ function commandFactory(id) {
|
||||
|
||||
commandArgs = command.readOptions(argv);
|
||||
|
||||
return withLogger(function (logger) {
|
||||
return withLogger(function(logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
@@ -37,16 +37,18 @@ function commandFactory(id) {
|
||||
function withLogger(func) {
|
||||
var logger = new Logger();
|
||||
|
||||
Q.try(func, logger)
|
||||
.done(function () {
|
||||
config.restore();
|
||||
var args = [].slice.call(arguments);
|
||||
args.unshift('end');
|
||||
logger.emit.apply(logger, args);
|
||||
}, function (error) {
|
||||
config.restore();
|
||||
logger.emit('error', error);
|
||||
});
|
||||
Q.try(func, logger).done(
|
||||
function() {
|
||||
config.restore();
|
||||
var args = [].slice.call(arguments);
|
||||
args.unshift('end');
|
||||
logger.emit.apply(logger, args);
|
||||
},
|
||||
function(error) {
|
||||
config.restore();
|
||||
logger.emit('error', error);
|
||||
}
|
||||
);
|
||||
|
||||
return logger;
|
||||
}
|
||||
@@ -56,11 +58,10 @@ function commandFactory(id) {
|
||||
return runApi;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
cache: {
|
||||
clean: commandFactory('./cache/clean'),
|
||||
list: commandFactory('./cache/list'),
|
||||
list: commandFactory('./cache/list')
|
||||
},
|
||||
help: commandFactory('./help'),
|
||||
home: commandFactory('./home'),
|
||||
|
||||
@@ -9,6 +9,13 @@ function info(logger, endpoint, property, config) {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle @ as version divider
|
||||
var splitParts = endpoint.split('/');
|
||||
splitParts[splitParts.length - 1] = splitParts[
|
||||
splitParts.length - 1
|
||||
].replace('@', '#');
|
||||
endpoint = splitParts.join('/');
|
||||
|
||||
var repository;
|
||||
var decEndpoint;
|
||||
|
||||
@@ -19,9 +26,10 @@ function info(logger, endpoint, property, config) {
|
||||
|
||||
return Q.all([
|
||||
getPkgMeta(repository, decEndpoint, property),
|
||||
decEndpoint.target === '*' && !property ? repository.versions(decEndpoint.source) : null
|
||||
])
|
||||
.spread(function (pkgMeta, versions) {
|
||||
decEndpoint.target === '*' && !property
|
||||
? repository.versions(decEndpoint.source)
|
||||
: null
|
||||
]).spread(function(pkgMeta, versions) {
|
||||
if (versions) {
|
||||
return {
|
||||
name: decEndpoint.source,
|
||||
@@ -35,24 +43,25 @@ function info(logger, endpoint, property, config) {
|
||||
}
|
||||
|
||||
function getPkgMeta(repository, decEndpoint, property) {
|
||||
return repository.fetch(decEndpoint)
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
pkgMeta = mout.object.filter(pkgMeta, function (value, key) {
|
||||
return key.charAt(0) !== '_';
|
||||
return repository
|
||||
.fetch(decEndpoint)
|
||||
.spread(function(canonicalDir, pkgMeta) {
|
||||
pkgMeta = mout.object.filter(pkgMeta, function(value, key) {
|
||||
return key.charAt(0) !== '_';
|
||||
});
|
||||
|
||||
// Retrieve specific property
|
||||
if (property) {
|
||||
pkgMeta = mout.object.get(pkgMeta, property);
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
});
|
||||
|
||||
// Retrieve specific property
|
||||
if (property) {
|
||||
pkgMeta = mout.object.get(pkgMeta, property);
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
info.readOptions = function (argv) {
|
||||
info.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var pkg = options.argv.remain[1];
|
||||
|
||||
@@ -23,31 +23,38 @@ function init(logger, config) {
|
||||
// This command requires interactive to be enabled
|
||||
if (!config.interactive) {
|
||||
throw createError('Register requires an interactive shell', 'ENOINT', {
|
||||
details: 'Note that you can manually force an interactive shell with --config.interactive'
|
||||
details:
|
||||
'Note that you can manually force an interactive shell with --config.interactive'
|
||||
});
|
||||
}
|
||||
|
||||
project = new Project(config, logger);
|
||||
|
||||
// Start with existing JSON details
|
||||
return readJson(project, logger)
|
||||
// Fill in defaults
|
||||
.then(setDefaults.bind(null, config))
|
||||
// Now prompt user to make changes
|
||||
.then(promptUser.bind(null, logger))
|
||||
// Set ignore based on the response
|
||||
.spread(setIgnore.bind(null, config))
|
||||
// Set dependencies based on the response
|
||||
.spread(setDependencies.bind(null, project))
|
||||
// All done!
|
||||
.spread(saveJson.bind(null, project, logger));
|
||||
return (
|
||||
readJson(project, logger)
|
||||
// Fill in defaults
|
||||
.then(setDefaults.bind(null, config))
|
||||
// Now prompt user to make changes
|
||||
.then(promptUser.bind(null, logger))
|
||||
// Set ignore based on the response
|
||||
.spread(setIgnore.bind(null, config))
|
||||
// Set dependencies based on the response
|
||||
.spread(setDependencies.bind(null, project))
|
||||
// All done!
|
||||
.spread(saveJson.bind(null, project, logger))
|
||||
);
|
||||
}
|
||||
|
||||
function readJson(project, logger) {
|
||||
return project.hasJson()
|
||||
.then(function (json) {
|
||||
return project.hasJson().then(function(json) {
|
||||
if (json) {
|
||||
logger.warn('existing', 'The existing ' + path.basename(json) + ' file will be used and filled in');
|
||||
logger.warn(
|
||||
'existing',
|
||||
'The existing ' +
|
||||
path.basename(json) +
|
||||
' file will be used and filled in'
|
||||
);
|
||||
}
|
||||
|
||||
return project.getJson();
|
||||
@@ -56,7 +63,7 @@ function readJson(project, logger) {
|
||||
|
||||
function saveJson(project, logger, json) {
|
||||
// Cleanup empty props (null values, empty strings, objects and arrays)
|
||||
mout.object.forOwn(json, function (value, key) {
|
||||
mout.object.forOwn(json, function(value, key) {
|
||||
if (!validConfigValue(value)) {
|
||||
delete json[key];
|
||||
}
|
||||
@@ -69,8 +76,7 @@ function saveJson(project, logger, json) {
|
||||
type: 'confirm',
|
||||
message: 'Looks good?',
|
||||
default: true
|
||||
})
|
||||
.then(function (good) {
|
||||
}).then(function(good) {
|
||||
if (!good) {
|
||||
return null;
|
||||
}
|
||||
@@ -84,9 +90,9 @@ function saveJson(project, logger, json) {
|
||||
// [0]: https://github.com/bower/bower.json-spec
|
||||
function validConfigValue(val) {
|
||||
return (
|
||||
mout.lang.isObject(val) ||
|
||||
mout.lang.isArray(val) ||
|
||||
mout.lang.isString(val) ||
|
||||
mout.lang.isObject(val) ||
|
||||
mout.lang.isArray(val) ||
|
||||
mout.lang.isString(val) ||
|
||||
mout.lang.isBoolean(val) ||
|
||||
mout.lang.isNumber(val)
|
||||
);
|
||||
@@ -116,52 +122,70 @@ function setDefaults(config, json) {
|
||||
// Homepage
|
||||
if (!json.homepage) {
|
||||
// Set as GitHub homepage if it's a GitHub repository
|
||||
promise = promise.then(function () {
|
||||
promise = promise.then(function() {
|
||||
return cmd('git', ['config', '--get', 'remote.origin.url'])
|
||||
.spread(function (stdout) {
|
||||
var pair;
|
||||
.spread(function(stdout) {
|
||||
var pair;
|
||||
|
||||
stdout = stdout.trim();
|
||||
if (!stdout) {
|
||||
return;
|
||||
}
|
||||
stdout = stdout.trim();
|
||||
if (!stdout) {
|
||||
return;
|
||||
}
|
||||
|
||||
pair = GitHubResolver.getOrgRepoPair(stdout);
|
||||
if (pair) {
|
||||
json.homepage = 'https://github.com/' + pair.org + '/' + pair.repo;
|
||||
}
|
||||
})
|
||||
.fail(function () { });
|
||||
pair = GitHubResolver.getOrgRepoPair(stdout);
|
||||
if (pair) {
|
||||
json.homepage =
|
||||
'https://github.com/' + pair.org + '/' + pair.repo;
|
||||
}
|
||||
})
|
||||
.fail(function() {});
|
||||
});
|
||||
}
|
||||
|
||||
if (!json.authors) {
|
||||
promise = promise.then(function () {
|
||||
promise = promise.then(function() {
|
||||
// Get the user name configured in git
|
||||
return cmd('git', ['config', '--get', '--global', 'user.name'])
|
||||
.spread(function (stdout) {
|
||||
var gitEmail;
|
||||
var gitName = stdout.trim();
|
||||
return cmd('git', [
|
||||
'config',
|
||||
'--get',
|
||||
'--global',
|
||||
'user.name'
|
||||
]).spread(
|
||||
function(stdout) {
|
||||
var gitEmail;
|
||||
var gitName = stdout.trim();
|
||||
|
||||
// Abort if no name specified
|
||||
if (!gitName) {
|
||||
return;
|
||||
}
|
||||
// Abort if no name specified
|
||||
if (!gitName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the user email configured in git
|
||||
return cmd('git', ['config', '--get', '--global', 'user.email'])
|
||||
.spread(function (stdout) {
|
||||
gitEmail = stdout.trim();
|
||||
}, function () {})
|
||||
.then(function () {
|
||||
json.authors = gitName;
|
||||
json.authors += gitEmail ? ' <' + gitEmail + '>' : '';
|
||||
});
|
||||
}, function () {});
|
||||
// Get the user email configured in git
|
||||
return cmd('git', [
|
||||
'config',
|
||||
'--get',
|
||||
'--global',
|
||||
'user.email'
|
||||
])
|
||||
.spread(
|
||||
function(stdout) {
|
||||
gitEmail = stdout.trim();
|
||||
},
|
||||
function() {}
|
||||
)
|
||||
.then(function() {
|
||||
json.authors = gitName;
|
||||
json.authors += gitEmail
|
||||
? ' <' + gitEmail + '>'
|
||||
: '';
|
||||
});
|
||||
},
|
||||
function() {}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(function () {
|
||||
return promise.then(function() {
|
||||
return json;
|
||||
});
|
||||
}
|
||||
@@ -169,69 +193,73 @@ function setDefaults(config, json) {
|
||||
function promptUser(logger, json) {
|
||||
var questions = [
|
||||
{
|
||||
'name': 'name',
|
||||
'message': 'name',
|
||||
'default': json.name,
|
||||
'type': 'input'
|
||||
name: 'name',
|
||||
message: 'name',
|
||||
default: json.name,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'description',
|
||||
'message': 'description',
|
||||
'default': json.description,
|
||||
'type': 'input'
|
||||
name: 'description',
|
||||
message: 'description',
|
||||
default: json.description,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'main',
|
||||
'message': 'main file',
|
||||
'default': json.main,
|
||||
'type': 'input'
|
||||
name: 'main',
|
||||
message: 'main file',
|
||||
default: json.main,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'keywords',
|
||||
'message': 'keywords',
|
||||
'default': json.keywords ? json.keywords.toString() : null,
|
||||
'type': 'input'
|
||||
name: 'keywords',
|
||||
message: 'keywords',
|
||||
default: json.keywords ? json.keywords.toString() : null,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'authors',
|
||||
'message': 'authors',
|
||||
'default': json.authors ? json.authors.toString() : null,
|
||||
'type': 'input'
|
||||
name: 'authors',
|
||||
message: 'authors',
|
||||
default: json.authors ? json.authors.toString() : null,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'license',
|
||||
'message': 'license',
|
||||
'default': json.license || 'MIT',
|
||||
'type': 'input'
|
||||
name: 'license',
|
||||
message: 'license',
|
||||
default: json.license || 'MIT',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'homepage',
|
||||
'message': 'homepage',
|
||||
'default': json.homepage,
|
||||
'type': 'input'
|
||||
name: 'homepage',
|
||||
message: 'homepage',
|
||||
default: json.homepage,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
'name': 'dependencies',
|
||||
'message': 'set currently installed components as dependencies?',
|
||||
'default': !mout.object.size(json.dependencies) && !mout.object.size(json.devDependencies),
|
||||
'type': 'confirm'
|
||||
name: 'dependencies',
|
||||
message: 'set currently installed components as dependencies?',
|
||||
default:
|
||||
!mout.object.size(json.dependencies) &&
|
||||
!mout.object.size(json.devDependencies),
|
||||
type: 'confirm'
|
||||
},
|
||||
{
|
||||
'name': 'ignore',
|
||||
'message': 'add commonly ignored files to ignore list?',
|
||||
'default': true,
|
||||
'type': 'confirm'
|
||||
name: 'ignore',
|
||||
message: 'add commonly ignored files to ignore list?',
|
||||
default: true,
|
||||
type: 'confirm'
|
||||
},
|
||||
{
|
||||
'name': 'private',
|
||||
'message': 'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
|
||||
'default': !!json.private,
|
||||
'type': 'confirm'
|
||||
name: 'private',
|
||||
message:
|
||||
'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
|
||||
default: !!json.private,
|
||||
type: 'confirm'
|
||||
}
|
||||
];
|
||||
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function (answers) {
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions).then(function(
|
||||
answers
|
||||
) {
|
||||
json.name = answers.name;
|
||||
json.description = answers.description;
|
||||
json.main = answers.main;
|
||||
@@ -249,12 +277,12 @@ function toArray(value, splitter) {
|
||||
var arr = value.split(splitter || /[\s,]/);
|
||||
|
||||
// Trim values
|
||||
arr = arr.map(function (item) {
|
||||
arr = arr.map(function(item) {
|
||||
return item.trim();
|
||||
});
|
||||
|
||||
// Filter empty values
|
||||
arr = arr.filter(function (item) {
|
||||
arr = arr.filter(function(item) {
|
||||
return !!item;
|
||||
});
|
||||
|
||||
@@ -278,13 +306,12 @@ function setIgnore(config, json, answers) {
|
||||
|
||||
function setDependencies(project, json, answers) {
|
||||
if (answers.dependencies) {
|
||||
return project.getTree()
|
||||
.spread(function (tree, flattened, extraneous) {
|
||||
return project.getTree().spread(function(tree, flattened, extraneous) {
|
||||
if (extraneous.length) {
|
||||
json.dependencies = {};
|
||||
|
||||
// Add extraneous as dependencies
|
||||
extraneous.forEach(function (extra) {
|
||||
extraneous.forEach(function(extra) {
|
||||
var jsonEndpoint;
|
||||
|
||||
// Skip linked packages
|
||||
@@ -292,7 +319,9 @@ function setDependencies(project, json, answers) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonEndpoint = endpointParser.decomposed2json(extra.endpoint);
|
||||
jsonEndpoint = endpointParser.decomposed2json(
|
||||
extra.endpoint
|
||||
);
|
||||
mout.object.mixIn(json.dependencies, jsonEndpoint);
|
||||
});
|
||||
}
|
||||
@@ -306,7 +335,7 @@ function setDependencies(project, json, answers) {
|
||||
|
||||
// -------------------
|
||||
|
||||
init.readOptions = function (argv) {
|
||||
init.readOptions = function(argv) {
|
||||
return [];
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,14 @@ function install(logger, endpoints, options, config) {
|
||||
|
||||
// Convert endpoints to decomposed endpoints
|
||||
endpoints = endpoints || [];
|
||||
decEndpoints = endpoints.map(function (endpoint) {
|
||||
decEndpoints = endpoints.map(function(endpoint) {
|
||||
// handle @ as version divider
|
||||
var splitParts = endpoint.split('/');
|
||||
splitParts[splitParts.length - 1] = splitParts[
|
||||
splitParts.length - 1
|
||||
].replace('@', '#');
|
||||
endpoint = splitParts.join('/');
|
||||
|
||||
return endpointParser.decompose(endpoint);
|
||||
});
|
||||
|
||||
@@ -24,16 +31,19 @@ function install(logger, endpoints, options, config) {
|
||||
|
||||
// -------------------
|
||||
|
||||
install.readOptions = function (argv) {
|
||||
install.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
'force-latest': { type: Boolean, shorthand: 'F'},
|
||||
'production': { type: Boolean, shorthand: 'p' },
|
||||
'save': { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' },
|
||||
'save-exact': { type: Boolean, shorthand: 'E' }
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
'force-latest': { type: Boolean, shorthand: 'F' },
|
||||
production: { type: Boolean, shorthand: 'p' },
|
||||
save: { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' },
|
||||
'save-exact': { type: Boolean, shorthand: 'E' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
var packages = options.argv.remain.slice(1);
|
||||
|
||||
|
||||
@@ -20,23 +20,24 @@ function linkSelf(logger, config) {
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return project.getJson()
|
||||
.then(function (json) {
|
||||
return project.getJson().then(function(json) {
|
||||
var src = config.cwd;
|
||||
var dst = path.join(config.storage.links, json.name);
|
||||
|
||||
// Delete previous link if any
|
||||
return Q.nfcall(rimraf, dst)
|
||||
// Link globally
|
||||
.then(function () {
|
||||
return createLink(src, dst);
|
||||
})
|
||||
.then(function () {
|
||||
return {
|
||||
src: src,
|
||||
dst: dst
|
||||
};
|
||||
});
|
||||
return (
|
||||
Q.nfcall(rimraf, dst)
|
||||
// Link globally
|
||||
.then(function() {
|
||||
return createLink(src, dst);
|
||||
})
|
||||
.then(function() {
|
||||
return {
|
||||
src: src,
|
||||
dst: dst
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,27 +54,29 @@ function linkTo(logger, name, localName, config) {
|
||||
dst = path.join(relativeToBaseDir(config.cwd)(config.directory), localName);
|
||||
|
||||
// Delete destination folder if any
|
||||
return Q.nfcall(rimraf, dst)
|
||||
// Link locally
|
||||
.then(function () {
|
||||
return createLink(src, dst);
|
||||
})
|
||||
// Install linked package deps
|
||||
.then(function () {
|
||||
return project.update([localName]);
|
||||
})
|
||||
.then(function (installed) {
|
||||
return {
|
||||
src: src,
|
||||
dst: dst,
|
||||
installed: installed
|
||||
};
|
||||
});
|
||||
return (
|
||||
Q.nfcall(rimraf, dst)
|
||||
// Link locally
|
||||
.then(function() {
|
||||
return createLink(src, dst);
|
||||
})
|
||||
// Install linked package deps
|
||||
.then(function() {
|
||||
return project.update([localName]);
|
||||
})
|
||||
.then(function(installed) {
|
||||
return {
|
||||
src: src,
|
||||
dst: dst,
|
||||
installed: installed
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
link.readOptions = function (argv) {
|
||||
link.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
@@ -11,39 +11,48 @@ function list(logger, options, config) {
|
||||
options = options || {};
|
||||
|
||||
// Make relative option true by default when used with paths
|
||||
if (options.paths && options.relative == null) {
|
||||
if (options.paths && options.relative == null) {
|
||||
options.relative = true;
|
||||
}
|
||||
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return project.getTree(options)
|
||||
.spread(function (tree, flattened) {
|
||||
return project.getTree(options).spread(function(tree, flattened) {
|
||||
// Relativize paths
|
||||
// Also normalize paths on windows
|
||||
project.walkTree(tree, function (node) {
|
||||
if (node.missing) {
|
||||
return;
|
||||
}
|
||||
project.walkTree(
|
||||
tree,
|
||||
function(node) {
|
||||
if (node.missing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
}
|
||||
}, true);
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(
|
||||
config.cwd,
|
||||
node.canonicalDir
|
||||
);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// Note that we need to to parse the flattened tree because it might
|
||||
// contain additional packages
|
||||
mout.object.forOwn(flattened, function (node) {
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (node.missing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
|
||||
node.canonicalDir = path.relative(
|
||||
config.cwd,
|
||||
node.canonicalDir
|
||||
);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
@@ -61,8 +70,7 @@ function list(logger, options, config) {
|
||||
}
|
||||
|
||||
// Check for new versions
|
||||
return checkVersions(project, tree, logger)
|
||||
.then(function () {
|
||||
return checkVersions(project, tree, logger).then(function() {
|
||||
return tree;
|
||||
});
|
||||
});
|
||||
@@ -74,32 +82,40 @@ function checkVersions(project, tree, logger) {
|
||||
var repository = project.getPackageRepository();
|
||||
|
||||
// Gather all nodes, ignoring linked nodes
|
||||
project.walkTree(tree, function (node) {
|
||||
if (!node.linked) {
|
||||
nodes.push(node);
|
||||
}
|
||||
}, true);
|
||||
project.walkTree(
|
||||
tree,
|
||||
function(node) {
|
||||
if (!node.linked) {
|
||||
nodes.push(node);
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
if (nodes.length) {
|
||||
logger.info('check-new', 'Checking for new versions of the project dependencies...');
|
||||
logger.info(
|
||||
'check-new',
|
||||
'Checking for new versions of the project dependencies...'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for new versions for each node
|
||||
promises = nodes.map(function (node) {
|
||||
promises = nodes.map(function(node) {
|
||||
var target = node.endpoint.target;
|
||||
|
||||
return repository.versions(node.endpoint.source)
|
||||
.then(function (versions) {
|
||||
node.versions = versions;
|
||||
return repository
|
||||
.versions(node.endpoint.source)
|
||||
.then(function(versions) {
|
||||
node.versions = versions;
|
||||
|
||||
// Do not check if node's target is not a valid semver one
|
||||
if (versions.length && semver.validRange(target)) {
|
||||
node.update = {
|
||||
target: semver.maxSatisfying(versions, target),
|
||||
latest: semver.maxSatisfying(versions, '*')
|
||||
};
|
||||
}
|
||||
});
|
||||
// Do not check if node's target is not a valid semver one
|
||||
if (versions.length && semver.validRange(target)) {
|
||||
node.update = {
|
||||
target: semver.maxSatisfying(versions, target),
|
||||
latest: semver.maxSatisfying(versions, '*')
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Set the versions also for the root node
|
||||
@@ -111,7 +127,7 @@ function checkVersions(project, tree, logger) {
|
||||
function paths(flattened) {
|
||||
var ret = {};
|
||||
|
||||
mout.object.forOwn(flattened, function (pkg, name) {
|
||||
mout.object.forOwn(flattened, function(pkg, name) {
|
||||
var main;
|
||||
|
||||
if (pkg.missing) {
|
||||
@@ -132,7 +148,7 @@ function paths(flattened) {
|
||||
}
|
||||
|
||||
// Concatenate each main entry with the canonical dir
|
||||
main = main.map(function (part) {
|
||||
main = main.map(function(part) {
|
||||
return normalize(path.join(pkg.canonicalDir, part).trim());
|
||||
});
|
||||
|
||||
@@ -150,13 +166,16 @@ function normalize(src) {
|
||||
|
||||
// -------------------
|
||||
|
||||
list.readOptions = function (argv) {
|
||||
list.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
'paths': { type: Boolean, shorthand: 'p' },
|
||||
'relative': { type: Boolean, shorthand: 'r' }
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
paths: { type: Boolean, shorthand: 'p' },
|
||||
relative: { type: Boolean, shorthand: 'r' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
|
||||
@@ -19,24 +19,28 @@ function login(logger, options, config) {
|
||||
} else {
|
||||
// This command requires interactive to be enabled
|
||||
if (!config.interactive) {
|
||||
logger.emit('error', createError('Login requires an interactive shell', 'ENOINT', {
|
||||
details: 'Note that you can manually force an interactive shell with --config.interactive'
|
||||
}));
|
||||
logger.emit(
|
||||
'error',
|
||||
createError('Login requires an interactive shell', 'ENOINT', {
|
||||
details:
|
||||
'Note that you can manually force an interactive shell with --config.interactive'
|
||||
})
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var questions = [
|
||||
{
|
||||
'name': 'username',
|
||||
'message': 'Username',
|
||||
'type': 'input',
|
||||
'default': configstore.get('username')
|
||||
name: 'username',
|
||||
message: 'Username',
|
||||
type: 'input',
|
||||
default: configstore.get('username')
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'message': 'Password',
|
||||
'type': 'password'
|
||||
name: 'password',
|
||||
message: 'Password',
|
||||
type: 'password'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -44,8 +48,9 @@ function login(logger, options, config) {
|
||||
version: '3.0.0'
|
||||
});
|
||||
|
||||
promise = Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function (answers) {
|
||||
promise = Q.nfcall(logger.prompt.bind(logger), questions).then(function(
|
||||
answers
|
||||
) {
|
||||
configstore.set('username', answers.username);
|
||||
|
||||
github.authenticate({
|
||||
@@ -56,66 +61,91 @@ function login(logger, options, config) {
|
||||
|
||||
return Q.ninvoke(github.authorization, 'create', {
|
||||
scopes: ['user', 'repo'],
|
||||
note: 'Bower command line client (' + (new Date()).toISOString() + ')'
|
||||
note:
|
||||
'Bower command line client (' +
|
||||
new Date().toISOString() +
|
||||
')'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(function (result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info('EAUTH', 'Logged in as ' + configstore.get('username'), {});
|
||||
return promise.then(
|
||||
function(result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info(
|
||||
'EAUTH',
|
||||
'Logged in as ' + configstore.get('username'),
|
||||
{}
|
||||
);
|
||||
|
||||
return result;
|
||||
}, function (error) {
|
||||
var message;
|
||||
return result;
|
||||
},
|
||||
function(error) {
|
||||
var message;
|
||||
|
||||
try {
|
||||
message = JSON.parse(error.message).message;
|
||||
} catch (e) {
|
||||
message = 'Authorization failed';
|
||||
}
|
||||
|
||||
var questions = [
|
||||
{
|
||||
'name': 'otpcode',
|
||||
'message': 'Two-Factor Auth Code',
|
||||
'type': 'input'
|
||||
try {
|
||||
message = JSON.parse(error.message).message;
|
||||
} catch (e) {
|
||||
message = 'Authorization failed';
|
||||
}
|
||||
];
|
||||
|
||||
if (message === 'Must specify two-factor authentication OTP code.') {
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function (answers) {
|
||||
return Q.ninvoke(github.authorization, 'create', {
|
||||
scopes: ['user', 'repo'],
|
||||
note: 'Bower command line client (' + (new Date()).toISOString() + ')',
|
||||
headers: {
|
||||
'X-GitHub-OTP': answers.otpcode
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info('EAUTH', 'Logged in as ' + configstore.get('username'), {});
|
||||
var questions = [
|
||||
{
|
||||
name: 'otpcode',
|
||||
message: 'Two-Factor Auth Code',
|
||||
type: 'input'
|
||||
}
|
||||
];
|
||||
|
||||
return result;
|
||||
}, function () {
|
||||
if (
|
||||
message === 'Must specify two-factor authentication OTP code.'
|
||||
) {
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function(answers) {
|
||||
return Q.ninvoke(github.authorization, 'create', {
|
||||
scopes: ['user', 'repo'],
|
||||
note:
|
||||
'Bower command line client (' +
|
||||
new Date().toISOString() +
|
||||
')',
|
||||
headers: {
|
||||
'X-GitHub-OTP': answers.otpcode
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(
|
||||
function(result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info(
|
||||
'EAUTH',
|
||||
'Logged in as ' + configstore.get('username'),
|
||||
{}
|
||||
);
|
||||
|
||||
return result;
|
||||
},
|
||||
function() {
|
||||
logger.emit('error', createError(message, 'EAUTH'));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
logger.emit('error', createError(message, 'EAUTH'));
|
||||
});
|
||||
} else {
|
||||
logger.emit('error', createError(message, 'EAUTH'));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
login.readOptions = function (argv) {
|
||||
login.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
token: { type: String, shorthand: 't' },
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
token: { type: String, shorthand: 't' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
|
||||
@@ -12,18 +12,21 @@ function lookup(logger, name, config) {
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), name)
|
||||
.then(function (entry) {
|
||||
return !entry ? null : {
|
||||
name: name,
|
||||
url: entry && entry.url
|
||||
};
|
||||
});
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), name).then(
|
||||
function(entry) {
|
||||
return !entry
|
||||
? null
|
||||
: {
|
||||
name: name,
|
||||
url: entry.url
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
lookup.readOptions = function (argv) {
|
||||
lookup.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
@@ -17,35 +17,40 @@ function clean(project, options, removed) {
|
||||
|
||||
// Continually call clean until there is no more extraneous
|
||||
// dependencies to remove
|
||||
return project.getTree(options)
|
||||
.spread(function (tree, flattened, extraneous) {
|
||||
var names = extraneous.map(function (extra) {
|
||||
return extra.endpoint.name;
|
||||
});
|
||||
return project
|
||||
.getTree(options)
|
||||
.spread(function(tree, flattened, extraneous) {
|
||||
var names = extraneous.map(function(extra) {
|
||||
return extra.endpoint.name;
|
||||
});
|
||||
|
||||
// Uninstall extraneous
|
||||
return project.uninstall(names, options)
|
||||
.then(function (uninstalled) {
|
||||
// Are we done?
|
||||
if (!mout.object.size(uninstalled)) {
|
||||
return removed;
|
||||
}
|
||||
// Uninstall extraneous
|
||||
return project
|
||||
.uninstall(names, options)
|
||||
.then(function(uninstalled) {
|
||||
// Are we done?
|
||||
if (!mout.object.size(uninstalled)) {
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Not yet, recurse!
|
||||
mout.object.mixIn(removed, uninstalled);
|
||||
return clean(project, options, removed);
|
||||
// Not yet, recurse!
|
||||
mout.object.mixIn(removed, uninstalled);
|
||||
return clean(project, options, removed);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
prune.readOptions = function (argv) {
|
||||
prune.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
'production': { type: Boolean, shorthand: 'p' },
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
production: { type: Boolean, shorthand: 'p' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
|
||||
@@ -4,25 +4,35 @@ var PackageRepository = require('../core/PackageRepository');
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function register(logger, name, url, config) {
|
||||
function register(logger, name, source, config) {
|
||||
var repository;
|
||||
var registryClient;
|
||||
var force;
|
||||
var url;
|
||||
var githubSourceRegex = /^\w[\w-]*\/\w[\w-]*$/;
|
||||
var getGithubUrl = function(source) {
|
||||
return 'git@github.com:' + source + '.git';
|
||||
};
|
||||
|
||||
config = defaultConfig(config);
|
||||
force = config.force;
|
||||
|
||||
name = (name || '').trim();
|
||||
url = (url || '').trim();
|
||||
source = (source || '').trim();
|
||||
|
||||
url = source.match(githubSourceRegex) ? getGithubUrl(source) : source;
|
||||
|
||||
// Bypass any cache
|
||||
config.offline = false;
|
||||
config.force = true;
|
||||
|
||||
return Q.try(function () {
|
||||
return Q.try(function() {
|
||||
// Verify name and url
|
||||
if (!name || !url) {
|
||||
throw createError('Usage: bower register <name> <url>', 'EINVFORMAT');
|
||||
throw createError(
|
||||
'Usage: bower register <name> <url>',
|
||||
'EINVFORMAT'
|
||||
);
|
||||
}
|
||||
|
||||
// Attempt to resolve the package referenced by the URL to ensure
|
||||
@@ -30,45 +40,54 @@ function register(logger, name, url, config) {
|
||||
repository = new PackageRepository(config, logger);
|
||||
return repository.fetch({ name: name, source: url, target: '*' });
|
||||
})
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
if (pkgMeta.private) {
|
||||
throw createError('The package you are trying to register is marked as private', 'EPRIV');
|
||||
}
|
||||
.spread(function(canonicalDir, pkgMeta) {
|
||||
if (pkgMeta.private) {
|
||||
throw createError(
|
||||
'The package you are trying to register is marked as private',
|
||||
'EPRIV'
|
||||
);
|
||||
}
|
||||
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Confirm if the user really wants to register
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'Registering a package will make it installable via the registry (' +
|
||||
chalk.cyan.underline(config.registry.register) + '), continue?',
|
||||
default: true
|
||||
// Confirm if the user really wants to register
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message:
|
||||
'Registering a package will make it installable via the registry (' +
|
||||
chalk.cyan.underline(config.registry.register) +
|
||||
'), continue?',
|
||||
default: true
|
||||
});
|
||||
})
|
||||
.then(function(result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('register', url, {
|
||||
name: name,
|
||||
url: url
|
||||
});
|
||||
|
||||
return Q.nfcall(
|
||||
registryClient.register.bind(registryClient),
|
||||
name,
|
||||
url
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('register', url, {
|
||||
name: name,
|
||||
url: url
|
||||
});
|
||||
|
||||
return Q.nfcall(registryClient.register.bind(registryClient), name, url);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
register.readOptions = function (argv) {
|
||||
register.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
|
||||
@@ -27,7 +27,7 @@ function search(logger, name, config) {
|
||||
|
||||
// -------------------
|
||||
|
||||
search.readOptions = function (argv) {
|
||||
search.readOptions = function(argv) {
|
||||
var options = cli.readOptions(argv);
|
||||
var terms = options.argv.remain.slice(1);
|
||||
|
||||
|
||||
@@ -14,54 +14,62 @@ function uninstall(logger, names, options, config) {
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return project.getTree(options)
|
||||
.spread(function (tree, flattened) {
|
||||
return project.getTree(options).spread(function(tree, flattened) {
|
||||
// Uninstall nodes
|
||||
return project.uninstall(names, options)
|
||||
// Clean out non-shared uninstalled dependencies
|
||||
.then(function (uninstalled) {
|
||||
var names = Object.keys(uninstalled);
|
||||
var children = [];
|
||||
return (
|
||||
project
|
||||
.uninstall(names, options)
|
||||
// Clean out non-shared uninstalled dependencies
|
||||
.then(function(uninstalled) {
|
||||
var names = Object.keys(uninstalled);
|
||||
var children = [];
|
||||
|
||||
// Grab the dependencies of packages that were uninstalled
|
||||
mout.object.forOwn(flattened, function (node) {
|
||||
if (names.indexOf(node.endpoint.name) !== -1) {
|
||||
children.push.apply(children, mout.object.keys(node.dependencies));
|
||||
}
|
||||
});
|
||||
// Grab the dependencies of packages that were uninstalled
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (names.indexOf(node.endpoint.name) !== -1) {
|
||||
children.push.apply(
|
||||
children,
|
||||
mout.object.keys(node.dependencies)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Clean them!
|
||||
return clean(project, children, uninstalled);
|
||||
});
|
||||
// Clean them!
|
||||
return clean(project, children, uninstalled);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function clean(project, names, removed) {
|
||||
removed = removed || {};
|
||||
|
||||
return project.getTree()
|
||||
.spread(function (tree, flattened) {
|
||||
return project.getTree().spread(function(tree, flattened) {
|
||||
var nodes = [];
|
||||
var dependantsCounter = {};
|
||||
|
||||
// Grab the nodes of each specified name
|
||||
mout.object.forOwn(flattened, function (node) {
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (names.indexOf(node.endpoint.name) !== -1) {
|
||||
nodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
// Walk the down the tree, gathering dependants of the packages
|
||||
project.walkTree(tree, function (node, nodeName) {
|
||||
if (names.indexOf(nodeName) !== -1) {
|
||||
dependantsCounter[nodeName] = dependantsCounter[nodeName] || 0;
|
||||
dependantsCounter[nodeName] += node.nrDependants;
|
||||
}
|
||||
}, true);
|
||||
|
||||
project.walkTree(
|
||||
tree,
|
||||
function(node, nodeName) {
|
||||
if (names.indexOf(nodeName) !== -1) {
|
||||
dependantsCounter[nodeName] =
|
||||
dependantsCounter[nodeName] || 0;
|
||||
dependantsCounter[nodeName] += node.nrDependants;
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// Filter out those that have no dependants
|
||||
nodes = nodes.filter(function (node) {
|
||||
nodes = nodes.filter(function(node) {
|
||||
return !dependantsCounter[node.endpoint.name];
|
||||
});
|
||||
|
||||
@@ -71,39 +79,48 @@ function clean(project, names, removed) {
|
||||
}
|
||||
|
||||
// Grab the nodes after filtering
|
||||
names = nodes.map(function (node) {
|
||||
names = nodes.map(function(node) {
|
||||
return node.endpoint.name;
|
||||
});
|
||||
|
||||
// Uninstall them
|
||||
return project.uninstall(names)
|
||||
// Clean out non-shared uninstalled dependencies
|
||||
.then(function (uninstalled) {
|
||||
var children;
|
||||
return (
|
||||
project
|
||||
.uninstall(names)
|
||||
// Clean out non-shared uninstalled dependencies
|
||||
.then(function(uninstalled) {
|
||||
var children;
|
||||
|
||||
mout.object.mixIn(removed, uninstalled);
|
||||
mout.object.mixIn(removed, uninstalled);
|
||||
|
||||
// Grab the dependencies of packages that were uninstalled
|
||||
children = [];
|
||||
nodes.forEach(function (node) {
|
||||
children.push.apply(children, mout.object.keys(node.dependencies));
|
||||
});
|
||||
// Grab the dependencies of packages that were uninstalled
|
||||
children = [];
|
||||
nodes.forEach(function(node) {
|
||||
children.push.apply(
|
||||
children,
|
||||
mout.object.keys(node.dependencies)
|
||||
);
|
||||
});
|
||||
|
||||
// Recurse!
|
||||
return clean(project, children, removed);
|
||||
});
|
||||
// Recurse!
|
||||
return clean(project, children, removed);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
uninstall.readOptions = function (argv) {
|
||||
uninstall.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
'save': { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' }
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
save: { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ var PackageRepository = require('../core/PackageRepository');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function unregister(logger, name, config) {
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
@@ -28,46 +27,58 @@ function unregister(logger, name, config) {
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
if (!config.accessToken) {
|
||||
return logger.emit('error',
|
||||
createError('Use "bower login" with collaborator credentials', 'EFORBIDDEN')
|
||||
return logger.emit(
|
||||
'error',
|
||||
createError(
|
||||
'Use "bower login" with collaborator credentials',
|
||||
'EFORBIDDEN'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Q.resolve()
|
||||
.then(function () {
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
.then(function() {
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'You are about to remove component "' + chalk.cyan.underline(name) + '" from the bower registry (' + chalk.cyan.underline(config.registry.register) + '). It is generally considered bad behavior to remove versions of a library that others are depending on. Are you really sure?',
|
||||
default: false
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message:
|
||||
'You are about to remove component "' +
|
||||
chalk.cyan.underline(name) +
|
||||
'" from the bower registry (' +
|
||||
chalk.cyan.underline(config.registry.register) +
|
||||
'). It is generally considered bad behavior to remove versions of a library that others are depending on. Are you really sure?',
|
||||
default: false
|
||||
});
|
||||
})
|
||||
.then(function(result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('unregister', name, { name: name });
|
||||
|
||||
return Q.nfcall(
|
||||
registryClient.unregister.bind(registryClient),
|
||||
name
|
||||
);
|
||||
})
|
||||
.then(function(result) {
|
||||
logger.info('Package unregistered', name);
|
||||
|
||||
return result;
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('unregister', name, { name: name });
|
||||
|
||||
return Q.nfcall(registryClient.unregister.bind(registryClient), name);
|
||||
})
|
||||
.then(function (result) {
|
||||
logger.info('Package unregistered', name);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
unregister.readOptions = function (argv) {
|
||||
unregister.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
|
||||
@@ -18,13 +18,16 @@ function update(logger, names, options, config) {
|
||||
|
||||
// -------------------
|
||||
|
||||
update.readOptions = function (argv) {
|
||||
update.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
'force-latest': { type: Boolean, shorthand: 'F' },
|
||||
'production': { type: Boolean, shorthand: 'p' }
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
'force-latest': { type: Boolean, shorthand: 'F' },
|
||||
production: { type: Boolean, shorthand: 'p' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
|
||||
@@ -4,123 +4,205 @@ var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var execFile = require('child_process').execFile;
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function version(logger, versionArg, options, config) {
|
||||
var project;
|
||||
|
||||
options = options || {};
|
||||
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return bump(project, versionArg, options.message);
|
||||
return bump(logger, config, versionArg, options.message);
|
||||
}
|
||||
|
||||
function bump(project, versionArg, message) {
|
||||
var cwd = project._config.cwd || process.cwd();
|
||||
function bump(logger, config, versionArg, message) {
|
||||
var cwd = config.cwd || process.cwd();
|
||||
var newVersion;
|
||||
var doGitCommit = false;
|
||||
|
||||
return checkGit(cwd)
|
||||
.then(function (hasGit) {
|
||||
doGitCommit = hasGit;
|
||||
})
|
||||
.then(project.getJson.bind(project))
|
||||
.then(function (json) {
|
||||
newVersion = getNewVersion(json.version, versionArg);
|
||||
json.version = newVersion;
|
||||
})
|
||||
.then(project.saveJson.bind(project))
|
||||
.then(function () {
|
||||
if (doGitCommit) {
|
||||
return gitCommitAndTag(cwd, newVersion, message);
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
console.log('v' + newVersion);
|
||||
return newVersion;
|
||||
});
|
||||
}
|
||||
|
||||
function getNewVersion(currentVersion, versionArg) {
|
||||
var newVersion = semver.valid(versionArg);
|
||||
if (!newVersion) {
|
||||
newVersion = semver.inc(currentVersion, versionArg);
|
||||
if (!versionArg) {
|
||||
throw createError('No <version> agrument provided', 'EREADOPTIONS');
|
||||
}
|
||||
if (!newVersion) {
|
||||
throw createError('Invalid version argument: `' + versionArg + '`. Usage: `bower version [<newversion> | major | minor | patch]`', 'EINVALIDVERSION');
|
||||
}
|
||||
if (currentVersion === newVersion) {
|
||||
throw createError('Version not changed', 'EVERSIONNOTCHANGED');
|
||||
}
|
||||
return newVersion;
|
||||
|
||||
return driver
|
||||
.check(cwd)
|
||||
.then(function() {
|
||||
return Q.all([driver.versions(cwd), driver.currentVersion(cwd)]);
|
||||
})
|
||||
.spread(function(versions, currentVersion) {
|
||||
currentVersion = currentVersion || '0.0.0';
|
||||
|
||||
if (semver.valid(versionArg)) {
|
||||
newVersion = semver.valid(versionArg);
|
||||
} else {
|
||||
newVersion = semver.inc(currentVersion, versionArg);
|
||||
|
||||
if (!newVersion) {
|
||||
throw createError(
|
||||
'Invalid <version> argument: ' + versionArg,
|
||||
'EINVALIDVERSION',
|
||||
{ version: versionArg }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
newVersion =
|
||||
currentVersion[0] === 'v' ? 'v' + newVersion : newVersion;
|
||||
|
||||
if (versions) {
|
||||
versions.forEach(function(version) {
|
||||
if (semver.eq(version, newVersion)) {
|
||||
throw createError(
|
||||
'Version exists: ' + newVersion,
|
||||
'EVERSIONEXISTS',
|
||||
{ versions: versions, newVersion: newVersion }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return driver.bump(cwd, newVersion, message).then(function() {
|
||||
return {
|
||||
oldVersion: currentVersion,
|
||||
newVersion: newVersion
|
||||
};
|
||||
});
|
||||
})
|
||||
.then(function(result) {
|
||||
logger.info(
|
||||
'version',
|
||||
'Bumped package version from ' +
|
||||
result.oldVersion +
|
||||
' to ' +
|
||||
result.newVersion,
|
||||
result
|
||||
);
|
||||
|
||||
return result.newVersion;
|
||||
});
|
||||
}
|
||||
|
||||
function checkGit(cwd) {
|
||||
var gitDir = path.join(cwd, '.git');
|
||||
return Q.nfcall(fs.stat, gitDir)
|
||||
.then(function (stat) {
|
||||
if (stat.isDirectory()) {
|
||||
return checkGitStatus(cwd);
|
||||
var driver = {
|
||||
check: function(cwd) {
|
||||
function checkGit(cwd) {
|
||||
var gitDir = path.join(cwd, '.git');
|
||||
return Q.nfcall(fs.stat, gitDir).then(
|
||||
function(stat) {
|
||||
if (stat.isDirectory()) {
|
||||
return checkGitStatus(cwd);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
function() {
|
||||
//Ignore not found .git directory
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}, function () {
|
||||
//Ignore not found .git directory
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function checkGitStatus(cwd) {
|
||||
return Q.nfcall(which, 'git')
|
||||
.fail(function (err) {
|
||||
err.code = 'ENOGIT';
|
||||
throw err;
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['status', '--porcelain'], {env: process.env, cwd: cwd});
|
||||
})
|
||||
.then(function (value) {
|
||||
var stdout = value[0];
|
||||
var lines = filterModifiedStatusLines(stdout);
|
||||
if (lines.length) {
|
||||
throw createError('Git working directory not clean.\n' + lines.join('\n'), 'EWORKINGDIRECTORYDIRTY');
|
||||
function checkGitStatus(cwd) {
|
||||
return Q.nfcall(which, 'git')
|
||||
.fail(function(err) {
|
||||
err.code = 'ENOGIT';
|
||||
throw err;
|
||||
})
|
||||
.then(function() {
|
||||
return Q.nfcall(
|
||||
execFile,
|
||||
'git',
|
||||
['status', '--porcelain'],
|
||||
{ env: process.env, cwd: cwd }
|
||||
);
|
||||
})
|
||||
.then(function(value) {
|
||||
var stdout = value[0];
|
||||
var lines = filterModifiedStatusLines(stdout);
|
||||
if (lines.length) {
|
||||
throw createError(
|
||||
'Version bump requires clean working directory',
|
||||
'EWORKINGDIRECTORYDIRTY'
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function filterModifiedStatusLines(stdout) {
|
||||
return stdout.trim().split('\n')
|
||||
.filter(function (line) {
|
||||
return line.trim() && !line.match(/^\?\? /);
|
||||
}).map(function (line) {
|
||||
return line.trim();
|
||||
});
|
||||
}
|
||||
function filterModifiedStatusLines(stdout) {
|
||||
return stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(function(line) {
|
||||
return line.trim() && !line.match(/^\?\? /);
|
||||
})
|
||||
.map(function(line) {
|
||||
return line.trim();
|
||||
});
|
||||
}
|
||||
|
||||
function gitCommitAndTag(cwd, newVersion, message) {
|
||||
var tag = 'v' + newVersion;
|
||||
message = message || tag;
|
||||
message = message.replace(/%s/g, newVersion);
|
||||
return Q.nfcall(execFile, 'git', ['add', 'bower.json'], {env: process.env, cwd: cwd})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['commit', '-m', message], {env: process.env, cwd: cwd});
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {env: process.env, cwd: cwd});
|
||||
});
|
||||
}
|
||||
return checkGit(cwd).then(function(hasGit) {
|
||||
if (!hasGit) {
|
||||
throw createError(
|
||||
'Version bump currently supports only git repositories',
|
||||
'ENOTGITREPOSITORY'
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
versions: function(cwd) {
|
||||
return Q.nfcall(execFile, 'git', ['tag'], {
|
||||
env: process.env,
|
||||
cwd: cwd
|
||||
}).then(
|
||||
function(res) {
|
||||
var versions = res[0].split(/\r?\n/).filter(semver.valid);
|
||||
|
||||
// -------------------
|
||||
return versions;
|
||||
},
|
||||
function() {
|
||||
return [];
|
||||
}
|
||||
);
|
||||
},
|
||||
currentVersion: function(cwd) {
|
||||
return Q.nfcall(execFile, 'git', ['describe', '--abbrev=0', '--tags'], {
|
||||
env: process.env,
|
||||
cwd: cwd
|
||||
}).then(
|
||||
function(res) {
|
||||
var version = res[0].split(/\r?\n/).filter(semver.valid)[0];
|
||||
|
||||
version.readOptions = function (argv) {
|
||||
return version;
|
||||
},
|
||||
function() {
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
},
|
||||
bump: function(cwd, tag, message) {
|
||||
message = message || tag;
|
||||
message = message.replace(/%s/g, tag);
|
||||
return Q.nfcall(
|
||||
execFile,
|
||||
'git',
|
||||
['commit', '-m', message, '--allow-empty'],
|
||||
{ env: process.env, cwd: cwd }
|
||||
).then(function() {
|
||||
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {
|
||||
env: process.env,
|
||||
cwd: cwd
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
version.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
'message': { type: String, shorthand: 'm'}
|
||||
}, argv);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
message: { type: String, shorthand: 'm' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
return [options.argv.remain[1], options];
|
||||
};
|
||||
|
||||
@@ -22,26 +22,26 @@ function readCachedConfig(cwd, overwrites) {
|
||||
|
||||
// If interactive is auto (null), guess its value
|
||||
if (config.interactive == null) {
|
||||
config.interactive = (
|
||||
process.bin === 'bower' &&
|
||||
tty.isatty(1) &&
|
||||
!process.env.CI
|
||||
);
|
||||
config.interactive =
|
||||
process.bin === 'bower' && tty.isatty(1) && !process.env.CI;
|
||||
}
|
||||
|
||||
// Merge common CLI options into the config
|
||||
if (process.bin === 'bower') {
|
||||
var cli = require('./util/cli');
|
||||
|
||||
object.mixIn(config, cli.readOptions({
|
||||
force: { type: Boolean, shorthand: 'f' },
|
||||
offline: { type: Boolean, shorthand: 'o' },
|
||||
verbose: { type: Boolean, shorthand: 'V' },
|
||||
quiet: { type: Boolean, shorthand: 'q' },
|
||||
loglevel: { type: String, shorthand: 'l' },
|
||||
json: { type: Boolean, shorthand: 'j' },
|
||||
silent: { type: Boolean, shorthand: 's' }
|
||||
}));
|
||||
object.mixIn(
|
||||
config,
|
||||
cli.readOptions({
|
||||
force: { type: Boolean, shorthand: 'f' },
|
||||
offline: { type: Boolean, shorthand: 'o' },
|
||||
verbose: { type: Boolean, shorthand: 'V' },
|
||||
quiet: { type: Boolean, shorthand: 'q' },
|
||||
loglevel: { type: String, shorthand: 'l' },
|
||||
json: { type: Boolean, shorthand: 'j' },
|
||||
silent: { type: Boolean, shorthand: 's' }
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@ var createError = require('../util/createError');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
|
||||
function PackageRepository(config, logger) {
|
||||
|
||||
var registryOptions;
|
||||
|
||||
this._config = config;
|
||||
@@ -24,7 +23,7 @@ function PackageRepository(config, logger) {
|
||||
|
||||
// -----------------
|
||||
|
||||
PackageRepository.prototype.fetch = function (decEndpoint) {
|
||||
PackageRepository.prototype.fetch = function(decEndpoint) {
|
||||
var logger;
|
||||
var that = this;
|
||||
var isTargetable;
|
||||
@@ -36,132 +35,192 @@ PackageRepository.prototype.fetch = function (decEndpoint) {
|
||||
// used to fetch
|
||||
logger = this._logger.geminate();
|
||||
// Intercept all logs, adding additional information
|
||||
logger.intercept(function (log) {
|
||||
logger.intercept(function(log) {
|
||||
that._extendLog(log, info);
|
||||
});
|
||||
|
||||
return this._getResolver(decEndpoint, logger)
|
||||
// Decide if we retrieve from the cache or not
|
||||
// Also decide if we validate the cached entry or not
|
||||
.then(function (resolver) {
|
||||
info.resolver = resolver;
|
||||
isTargetable = resolver.constructor.isTargetable;
|
||||
return (
|
||||
this._getResolver(decEndpoint, logger)
|
||||
// Decide if we retrieve from the cache or not
|
||||
// Also decide if we validate the cached entry or not
|
||||
.then(function(resolver) {
|
||||
info.resolver = resolver;
|
||||
isTargetable = resolver.constructor.isTargetable;
|
||||
|
||||
if (!resolver.isCacheable()) {
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
// If force flag is used, bypass cache, but write to cache anyway
|
||||
if (that._config.force) {
|
||||
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
// Note that we use the resolver methods to query the
|
||||
// cache because transformations/normalisations can occur
|
||||
return that._resolveCache.retrieve(resolver.getSource(), resolver.getTarget())
|
||||
// Decide if we can use the one from the resolve cache
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
// If there's no package in the cache
|
||||
if (!canonicalDir) {
|
||||
// And the offline flag is passed, error out
|
||||
if (that._config.offline) {
|
||||
throw createError('No cached version for ' + resolver.getSource() + '#' + resolver.getTarget(), 'ENOCACHE', {
|
||||
resolver: resolver
|
||||
});
|
||||
if (!resolver.isCacheable()) {
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
// Otherwise, we have to resolve it
|
||||
logger.info('not-cached', resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
|
||||
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
|
||||
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
info.canonicalDir = canonicalDir;
|
||||
info.pkgMeta = pkgMeta;
|
||||
|
||||
logger.info('cached', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
|
||||
|
||||
// If offline flag is used, use directly the cached one
|
||||
if (that._config.offline) {
|
||||
return [canonicalDir, pkgMeta, isTargetable];
|
||||
}
|
||||
|
||||
// Otherwise check for new contents
|
||||
logger.action('validate', (pkgMeta._release ? pkgMeta._release + ' against ' : '') +
|
||||
resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
|
||||
|
||||
return resolver.hasNew(pkgMeta)
|
||||
.then(function (hasNew) {
|
||||
// If there are no new contents, resolve to
|
||||
// the cached one
|
||||
if (!hasNew) {
|
||||
return [canonicalDir, pkgMeta, isTargetable];
|
||||
// If force flag is used, bypass cache, but write to cache anyway
|
||||
if (that._config.force) {
|
||||
logger.action(
|
||||
'resolve',
|
||||
resolver.getSource() + '#' + resolver.getTarget()
|
||||
);
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
// Otherwise resolve to the newest one
|
||||
logger.info('new', 'version for ' + resolver.getSource() + '#' + resolver.getTarget());
|
||||
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
|
||||
// Note that we use the resolver methods to query the
|
||||
// cache because transformations/normalisations can occur
|
||||
return (
|
||||
that._resolveCache
|
||||
.retrieve(resolver.getSource(), resolver.getTarget())
|
||||
// Decide if we can use the one from the resolve cache
|
||||
.spread(function(canonicalDir, pkgMeta) {
|
||||
// If there's no package in the cache
|
||||
if (!canonicalDir) {
|
||||
// And the offline flag is passed, error out
|
||||
if (that._config.offline) {
|
||||
throw createError(
|
||||
'No cached version for ' +
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget(),
|
||||
'ENOCACHE',
|
||||
{
|
||||
resolver: resolver
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return that._resolve(resolver, logger);
|
||||
});
|
||||
});
|
||||
})
|
||||
// If something went wrong, also extend the error
|
||||
.fail(function (err) {
|
||||
that._extendLog(err, info);
|
||||
throw err;
|
||||
});
|
||||
// Otherwise, we have to resolve it
|
||||
logger.info(
|
||||
'not-cached',
|
||||
resolver.getSource() +
|
||||
(resolver.getTarget()
|
||||
? '#' + resolver.getTarget()
|
||||
: '')
|
||||
);
|
||||
logger.action(
|
||||
'resolve',
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget()
|
||||
);
|
||||
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
info.canonicalDir = canonicalDir;
|
||||
info.pkgMeta = pkgMeta;
|
||||
|
||||
logger.info(
|
||||
'cached',
|
||||
resolver.getSource() +
|
||||
(pkgMeta._release
|
||||
? '#' + pkgMeta._release
|
||||
: '')
|
||||
);
|
||||
|
||||
// If offline flag is used, use directly the cached one
|
||||
if (that._config.offline) {
|
||||
return [canonicalDir, pkgMeta, isTargetable];
|
||||
}
|
||||
|
||||
// Otherwise check for new contents
|
||||
logger.action(
|
||||
'validate',
|
||||
(pkgMeta._release
|
||||
? pkgMeta._release + ' against '
|
||||
: '') +
|
||||
resolver.getSource() +
|
||||
(resolver.getTarget()
|
||||
? '#' + resolver.getTarget()
|
||||
: '')
|
||||
);
|
||||
|
||||
return resolver
|
||||
.hasNew(pkgMeta)
|
||||
.then(function(hasNew) {
|
||||
// If there are no new contents, resolve to
|
||||
// the cached one
|
||||
if (!hasNew) {
|
||||
return [
|
||||
canonicalDir,
|
||||
pkgMeta,
|
||||
isTargetable
|
||||
];
|
||||
}
|
||||
|
||||
// Otherwise resolve to the newest one
|
||||
logger.info(
|
||||
'new',
|
||||
'version for ' +
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget()
|
||||
);
|
||||
logger.action(
|
||||
'resolve',
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget()
|
||||
);
|
||||
|
||||
return that._resolve(resolver, logger);
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
// If something went wrong, also extend the error
|
||||
.fail(function(err) {
|
||||
that._extendLog(err, info);
|
||||
throw err;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.versions = function (source) {
|
||||
PackageRepository.prototype.versions = function(source) {
|
||||
// Resolve the source using the factory because the
|
||||
// source can actually be a registry name
|
||||
return this._getResolver({ source: source })
|
||||
.then(function (resolver) {
|
||||
// If offline, resolve using the cached versions
|
||||
if (this._config.offline) {
|
||||
return this._resolveCache.versions(resolver.getSource());
|
||||
}
|
||||
return this._getResolver({ source: source }).then(
|
||||
function(resolver) {
|
||||
// If offline, resolve using the cached versions
|
||||
if (this._config.offline) {
|
||||
return this._resolveCache.versions(resolver.getSource());
|
||||
}
|
||||
|
||||
// Otherwise, fetch remotely
|
||||
return resolver.constructor.versions(resolver.getSource());
|
||||
}.bind(this));
|
||||
// Otherwise, fetch remotely
|
||||
return resolver.constructor.versions(resolver.getSource());
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.eliminate = function (pkgMeta) {
|
||||
PackageRepository.prototype.eliminate = function(pkgMeta) {
|
||||
return Q.all([
|
||||
this._resolveCache.eliminate(pkgMeta),
|
||||
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient), pkgMeta.name)
|
||||
Q.nfcall(
|
||||
this._registryClient.clearCache.bind(this._registryClient),
|
||||
pkgMeta.name
|
||||
)
|
||||
]);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.clear = function () {
|
||||
PackageRepository.prototype.clear = function() {
|
||||
return Q.all([
|
||||
this._resolveCache.clear(),
|
||||
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient))
|
||||
]);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.reset = function () {
|
||||
PackageRepository.prototype.reset = function() {
|
||||
this._resolveCache.reset();
|
||||
this._registryClient.resetCache();
|
||||
};
|
||||
|
||||
PackageRepository.prototype.list = function () {
|
||||
PackageRepository.prototype.list = function() {
|
||||
return this._resolveCache.list();
|
||||
};
|
||||
|
||||
PackageRepository.prototype.getRegistryClient = function () {
|
||||
PackageRepository.prototype.getRegistryClient = function() {
|
||||
return this._registryClient;
|
||||
};
|
||||
|
||||
PackageRepository.prototype.getResolveCache = function () {
|
||||
PackageRepository.prototype.getResolveCache = function() {
|
||||
return this._resolveCache;
|
||||
};
|
||||
|
||||
PackageRepository.clearRuntimeCache = function () {
|
||||
PackageRepository.clearRuntimeCache = function() {
|
||||
ResolveCache.clearRuntimeCache();
|
||||
RegistryClient.clearRuntimeCache();
|
||||
resolverFactory.clearRuntimeCache();
|
||||
@@ -169,41 +228,59 @@ PackageRepository.clearRuntimeCache = function () {
|
||||
|
||||
// ---------------------
|
||||
|
||||
PackageRepository.prototype._getResolver = function (decEndpoint, logger) {
|
||||
PackageRepository.prototype._getResolver = function(decEndpoint, logger) {
|
||||
logger = logger || this._logger;
|
||||
|
||||
// Get the appropriate resolver
|
||||
return resolverFactory(decEndpoint, { config: this._config, logger: logger }, this._registryClient);
|
||||
return resolverFactory(
|
||||
decEndpoint,
|
||||
{ config: this._config, logger: logger },
|
||||
this._registryClient
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype._resolve = function (resolver, logger) {
|
||||
PackageRepository.prototype._resolve = function(resolver, logger) {
|
||||
var that = this;
|
||||
|
||||
// Resolve the resolver
|
||||
return resolver.resolve()
|
||||
// Store in the cache
|
||||
.then(function (canonicalDir) {
|
||||
if (!resolver.isCacheable()) {
|
||||
return canonicalDir;
|
||||
}
|
||||
return (
|
||||
resolver
|
||||
.resolve()
|
||||
// Store in the cache
|
||||
.then(function(canonicalDir) {
|
||||
if (!resolver.isCacheable()) {
|
||||
return canonicalDir;
|
||||
}
|
||||
|
||||
return that._resolveCache.store(canonicalDir, resolver.getPkgMeta());
|
||||
})
|
||||
// Resolve promise with canonical dir and package meta
|
||||
.then(function (dir) {
|
||||
var pkgMeta = resolver.getPkgMeta();
|
||||
return that._resolveCache.store(
|
||||
canonicalDir,
|
||||
resolver.getPkgMeta()
|
||||
);
|
||||
})
|
||||
// Resolve promise with canonical dir and package meta
|
||||
.then(function(dir) {
|
||||
var pkgMeta = resolver.getPkgMeta();
|
||||
|
||||
logger.info('resolved', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
|
||||
return [dir, pkgMeta, resolver.constructor.isTargetable()];
|
||||
});
|
||||
logger.info(
|
||||
'resolved',
|
||||
resolver.getSource() +
|
||||
(pkgMeta._release ? '#' + pkgMeta._release : '')
|
||||
);
|
||||
return [dir, pkgMeta, resolver.constructor.isTargetable()];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype._extendLog = function (log, info) {
|
||||
PackageRepository.prototype._extendLog = function(log, info) {
|
||||
log.data = log.data || {};
|
||||
|
||||
// Store endpoint info in each log
|
||||
if (info.decEndpoint) {
|
||||
log.data.endpoint = mout.object.pick(info.decEndpoint, ['name', 'source', 'target']);
|
||||
log.data.endpoint = mout.object.pick(info.decEndpoint, [
|
||||
'name',
|
||||
'source',
|
||||
'target'
|
||||
]);
|
||||
}
|
||||
|
||||
// Store the resolver info in each log
|
||||
|
||||
1272
lib/core/Project.js
1272
lib/core/Project.js
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ function ResolveCache(config) {
|
||||
if (!this._cache) {
|
||||
this._cache = new LRU({
|
||||
max: 100,
|
||||
maxAge: 60 * 5 * 1000 // 5 minutes
|
||||
maxAge: 60 * 5 * 1000 // 5 minutes
|
||||
});
|
||||
this.constructor._cache.set(this._dir, this._cache);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ function ResolveCache(config) {
|
||||
|
||||
// -----------------
|
||||
|
||||
ResolveCache.prototype.retrieve = function (source, target) {
|
||||
ResolveCache.prototype.retrieve = function(source, target) {
|
||||
var sourceId = md5(source);
|
||||
var dir = path.join(this._dir, sourceId);
|
||||
var that = this;
|
||||
@@ -48,56 +48,57 @@ ResolveCache.prototype.retrieve = function (source, target) {
|
||||
target = target || '*';
|
||||
|
||||
return this._getVersions(sourceId)
|
||||
.spread(function (versions) {
|
||||
var suitable;
|
||||
.spread(function(versions) {
|
||||
var suitable;
|
||||
|
||||
// If target is a semver, find a suitable version
|
||||
if (semver.validRange(target)) {
|
||||
suitable = semver.maxSatisfying(versions, target, true);
|
||||
// If target is a semver, find a suitable version
|
||||
if (semver.validRange(target)) {
|
||||
suitable = semver.maxSatisfying(versions, target, true);
|
||||
|
||||
if (suitable) {
|
||||
return suitable;
|
||||
if (suitable) {
|
||||
return suitable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If target is '*' check if there's a cached '_wildcard'
|
||||
if (target === '*') {
|
||||
return mout.array.find(versions, function (version) {
|
||||
return version === '_wildcard';
|
||||
// If target is '*' check if there's a cached '_wildcard'
|
||||
if (target === '*') {
|
||||
return mout.array.find(versions, function(version) {
|
||||
return version === '_wildcard';
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise check if there's an exact match
|
||||
return mout.array.find(versions, function(version) {
|
||||
return version === target;
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function(version) {
|
||||
var canonicalDir;
|
||||
|
||||
// Otherwise check if there's an exact match
|
||||
return mout.array.find(versions, function (version) {
|
||||
return version === target;
|
||||
if (!version) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Resolve with canonical dir and package meta
|
||||
canonicalDir = path.join(dir, encodeURIComponent(version));
|
||||
return that._readPkgMeta(canonicalDir).then(
|
||||
function(pkgMeta) {
|
||||
return [canonicalDir, pkgMeta];
|
||||
},
|
||||
function() {
|
||||
// If there was an error, invalidate the in-memory cache,
|
||||
// delete the cached package and try again
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.nfcall(rimraf, canonicalDir).then(function() {
|
||||
return that.retrieve(source, target);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(function (version) {
|
||||
var canonicalDir;
|
||||
|
||||
if (!version) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Resolve with canonical dir and package meta
|
||||
canonicalDir = path.join(dir, encodeURIComponent(version));
|
||||
return that._readPkgMeta(canonicalDir)
|
||||
.then(function (pkgMeta) {
|
||||
return [canonicalDir, pkgMeta];
|
||||
}, function () {
|
||||
// If there was an error, invalidate the in-memory cache,
|
||||
// delete the cached package and try again
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.nfcall(rimraf, canonicalDir)
|
||||
.then(function () {
|
||||
return that.retrieve(source, target);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
|
||||
ResolveCache.prototype.store = function(canonicalDir, pkgMeta) {
|
||||
var sourceId;
|
||||
var release;
|
||||
var dir;
|
||||
@@ -108,70 +109,82 @@ ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
|
||||
promise = pkgMeta ? Q.resolve(pkgMeta) : this._readPkgMeta(canonicalDir);
|
||||
|
||||
return promise
|
||||
.then(function (pkgMeta) {
|
||||
sourceId = md5(pkgMeta._source);
|
||||
release = that._getPkgRelease(pkgMeta);
|
||||
dir = path.join(that._dir, sourceId, release);
|
||||
pkgLock = path.join(that._lockDir, sourceId + '-' + release + '.lock');
|
||||
.then(function(pkgMeta) {
|
||||
sourceId = md5(pkgMeta._source);
|
||||
release = that._getPkgRelease(pkgMeta);
|
||||
dir = path.join(that._dir, sourceId, release);
|
||||
pkgLock = path.join(
|
||||
that._lockDir,
|
||||
sourceId + '-' + release + '.lock'
|
||||
);
|
||||
|
||||
// Check if destination directory exists to prevent issuing lock at all times
|
||||
return Q.nfcall(fs.stat, dir)
|
||||
.fail(function (err) {
|
||||
var lockParams = { wait: 250, retries: 25, stale: 60000 };
|
||||
return Q.nfcall(lockFile.lock, pkgLock, lockParams).then(function () {
|
||||
// Ensure other process didn't start copying files before lock was created
|
||||
return Q.nfcall(fs.stat, dir)
|
||||
.fail(function (err) {
|
||||
// If stat fails, it is expected to return ENOENT
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
// Check if destination directory exists to prevent issuing lock at all times
|
||||
return Q.nfcall(fs.stat, dir)
|
||||
.fail(function(err) {
|
||||
var lockParams = { wait: 250, retries: 25, stale: 60000 };
|
||||
return Q.nfcall(lockFile.lock, pkgLock, lockParams)
|
||||
.then(function() {
|
||||
// Ensure other process didn't start copying files before lock was created
|
||||
return Q.nfcall(fs.stat, dir).fail(function(err) {
|
||||
// If stat fails, it is expected to return ENOENT
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Create missing directory and copy files there
|
||||
return Q.nfcall(mkdirp, path.dirname(dir)).then(function () {
|
||||
return Q.nfcall(fs.rename, canonicalDir, dir)
|
||||
.fail(function (err) {
|
||||
// If error is EXDEV it means that we are trying to rename
|
||||
// across different drives, so we copy and remove it instead
|
||||
if (err.code !== 'EXDEV') {
|
||||
throw err;
|
||||
}
|
||||
// Create missing directory and copy files there
|
||||
return Q.nfcall(mkdirp, path.dirname(dir)).then(
|
||||
function() {
|
||||
return Q.nfcall(
|
||||
fs.rename,
|
||||
canonicalDir,
|
||||
dir
|
||||
).fail(function(err) {
|
||||
// If error is EXDEV it means that we are trying to rename
|
||||
// across different drives, so we copy and remove it instead
|
||||
if (err.code !== 'EXDEV') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return copy.copyDir(canonicalDir, dir);
|
||||
return copy.copyDir(
|
||||
canonicalDir,
|
||||
dir
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.finally(function() {
|
||||
lockFile.unlockSync(pkgLock);
|
||||
});
|
||||
});
|
||||
})
|
||||
.finally(function() {
|
||||
// Ensure no tmp dir is left on disk.
|
||||
return Q.nfcall(rimraf, canonicalDir);
|
||||
});
|
||||
}).finally(function () {
|
||||
lockFile.unlockSync(pkgLock);
|
||||
});
|
||||
}).finally(function () {
|
||||
// Ensure no tmp dir is left on disk.
|
||||
return Q.nfcall(rimraf, canonicalDir);
|
||||
})
|
||||
.then(function() {
|
||||
var versions = that._cache.get(sourceId);
|
||||
|
||||
// Add it to the in memory cache
|
||||
// and sort the versions afterwards
|
||||
if (versions && versions.indexOf(release) === -1) {
|
||||
versions.push(release);
|
||||
that._sortVersions(versions);
|
||||
}
|
||||
|
||||
// Resolve with the final location
|
||||
return dir;
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
var versions = that._cache.get(sourceId);
|
||||
|
||||
// Add it to the in memory cache
|
||||
// and sort the versions afterwards
|
||||
if (versions && versions.indexOf(release) === -1) {
|
||||
versions.push(release);
|
||||
that._sortVersions(versions);
|
||||
}
|
||||
|
||||
// Resolve with the final location
|
||||
return dir;
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.eliminate = function (pkgMeta) {
|
||||
ResolveCache.prototype.eliminate = function(pkgMeta) {
|
||||
var sourceId = md5(pkgMeta._source);
|
||||
var release = this._getPkgRelease(pkgMeta);
|
||||
var dir = path.join(this._dir, sourceId, release);
|
||||
var that = this;
|
||||
|
||||
return Q.nfcall(rimraf, dir)
|
||||
.then(function () {
|
||||
return Q.nfcall(rimraf, dir).then(function() {
|
||||
var versions = that._cache.get(sourceId) || [];
|
||||
mout.array.remove(versions, release);
|
||||
|
||||
@@ -182,8 +195,7 @@ ResolveCache.prototype.eliminate = function (pkgMeta) {
|
||||
if (!versions.length) {
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return that._getVersions(sourceId)
|
||||
.spread(function (versions) {
|
||||
return that._getVersions(sourceId).spread(function(versions) {
|
||||
if (!versions.length) {
|
||||
// Do not keep in-memory cache if it's completely
|
||||
// empty
|
||||
@@ -196,125 +208,144 @@ ResolveCache.prototype.eliminate = function (pkgMeta) {
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.clear = function () {
|
||||
ResolveCache.prototype.clear = function() {
|
||||
return Q.nfcall(rimraf, this._dir)
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.mkdir, this._dir);
|
||||
}.bind(this))
|
||||
.then(function () {
|
||||
this._cache.reset();
|
||||
}.bind(this));
|
||||
.then(
|
||||
function() {
|
||||
return Q.nfcall(fs.mkdir, this._dir);
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function() {
|
||||
this._cache.reset();
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
ResolveCache.prototype.reset = function () {
|
||||
ResolveCache.prototype.reset = function() {
|
||||
this._cache.reset();
|
||||
return this;
|
||||
};
|
||||
|
||||
ResolveCache.prototype.versions = function (source) {
|
||||
ResolveCache.prototype.versions = function(source) {
|
||||
var sourceId = md5(source);
|
||||
|
||||
return this._getVersions(sourceId)
|
||||
.spread(function (versions) {
|
||||
return versions.filter(function (version) {
|
||||
return this._getVersions(sourceId).spread(function(versions) {
|
||||
return versions.filter(function(version) {
|
||||
return semver.valid(version);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.list = function () {
|
||||
ResolveCache.prototype.list = function() {
|
||||
var promises;
|
||||
var dirs = [];
|
||||
var that = this;
|
||||
|
||||
// Get the list of directories
|
||||
return Q.nfcall(fs.readdir, this._dir)
|
||||
.then(function (sourceIds) {
|
||||
promises = sourceIds.map(function (sourceId) {
|
||||
return Q.nfcall(fs.readdir, path.join(that._dir, sourceId))
|
||||
.then(function (versions) {
|
||||
versions.forEach(function (version) {
|
||||
var dir = path.join(that._dir, sourceId, version);
|
||||
dirs.push(dir);
|
||||
return (
|
||||
Q.nfcall(fs.readdir, this._dir)
|
||||
.then(function(sourceIds) {
|
||||
promises = sourceIds.map(function(sourceId) {
|
||||
return Q.nfcall(
|
||||
fs.readdir,
|
||||
path.join(that._dir, sourceId)
|
||||
).then(
|
||||
function(versions) {
|
||||
versions.forEach(function(version) {
|
||||
var dir = path.join(
|
||||
that._dir,
|
||||
sourceId,
|
||||
version
|
||||
);
|
||||
dirs.push(dir);
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
// Ignore lurking files, e.g.: .DS_Store if the user
|
||||
// has navigated throughout the cache
|
||||
if (err.code === 'ENOTDIR' && err.path) {
|
||||
return Q.nfcall(rimraf, err.path);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
});
|
||||
}, function (err) {
|
||||
// Ignore lurking files, e.g.: .DS_Store if the user
|
||||
// has navigated throughout the cache
|
||||
if (err.code === 'ENOTDIR' && err.path) {
|
||||
return Q.nfcall(rimraf, err.path);
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
return Q.all(promises);
|
||||
})
|
||||
// Read every package meta
|
||||
.then(function() {
|
||||
promises = dirs.map(function(dir) {
|
||||
return that._readPkgMeta(dir).then(
|
||||
function(pkgMeta) {
|
||||
return {
|
||||
canonicalDir: dir,
|
||||
pkgMeta: pkgMeta
|
||||
};
|
||||
},
|
||||
function() {
|
||||
// If it fails to read, invalidate the in memory
|
||||
// cache for the source and delete the entry directory
|
||||
var sourceId = path.basename(path.dirname(dir));
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
// Read every package meta
|
||||
.then(function () {
|
||||
promises = dirs.map(function (dir) {
|
||||
return that._readPkgMeta(dir)
|
||||
.then(function (pkgMeta) {
|
||||
return {
|
||||
canonicalDir: dir,
|
||||
pkgMeta: pkgMeta
|
||||
};
|
||||
}, function () {
|
||||
// If it fails to read, invalidate the in memory
|
||||
// cache for the source and delete the entry directory
|
||||
var sourceId = path.basename(path.dirname(dir));
|
||||
that._cache.del(sourceId);
|
||||
return Q.nfcall(rimraf, dir);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Q.nfcall(rimraf, dir);
|
||||
});
|
||||
});
|
||||
return Q.all(promises);
|
||||
})
|
||||
// Sort by name ASC & release ASC
|
||||
.then(function(entries) {
|
||||
// Ignore falsy entries due to errors reading
|
||||
// package metas
|
||||
entries = entries.filter(function(entry) {
|
||||
return !!entry;
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
// Sort by name ASC & release ASC
|
||||
.then(function (entries) {
|
||||
// Ignore falsy entries due to errors reading
|
||||
// package metas
|
||||
entries = entries.filter(function (entry) {
|
||||
return !!entry;
|
||||
});
|
||||
return entries.sort(function(entry1, entry2) {
|
||||
var pkgMeta1 = entry1.pkgMeta;
|
||||
var pkgMeta2 = entry2.pkgMeta;
|
||||
var comp = pkgMeta1.name.localeCompare(pkgMeta2.name);
|
||||
|
||||
return entries.sort(function (entry1, entry2) {
|
||||
var pkgMeta1 = entry1.pkgMeta;
|
||||
var pkgMeta2 = entry2.pkgMeta;
|
||||
var comp = pkgMeta1.name.localeCompare(pkgMeta2.name);
|
||||
// Sort by name
|
||||
if (comp) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
// Sort by name
|
||||
if (comp) {
|
||||
return comp;
|
||||
}
|
||||
// Sort by version
|
||||
if (pkgMeta1.version && pkgMeta2.version) {
|
||||
return semver.compare(
|
||||
pkgMeta1.version,
|
||||
pkgMeta2.version
|
||||
);
|
||||
}
|
||||
if (pkgMeta1.version) {
|
||||
return -1;
|
||||
}
|
||||
if (pkgMeta2.version) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Sort by version
|
||||
if (pkgMeta1.version && pkgMeta2.version) {
|
||||
return semver.compare(pkgMeta1.version, pkgMeta2.version);
|
||||
}
|
||||
if (pkgMeta1.version) {
|
||||
return -1;
|
||||
}
|
||||
if (pkgMeta2.version) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Sort by target
|
||||
return pkgMeta1._target.localeCompare(pkgMeta2._target);
|
||||
});
|
||||
});
|
||||
// Sort by target
|
||||
return pkgMeta1._target.localeCompare(pkgMeta2._target);
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache.clearRuntimeCache = function () {
|
||||
ResolveCache.clearRuntimeCache = function() {
|
||||
// Note that _cache refers to the static _cache variable
|
||||
// that holds other caches per dir!
|
||||
// Do not confuse it with the instance cache
|
||||
|
||||
// Clear cache of each directory
|
||||
this._cache.forEach(function (cache) {
|
||||
this._cache.forEach(function(cache) {
|
||||
cache.reset();
|
||||
});
|
||||
|
||||
@@ -324,8 +355,10 @@ ResolveCache.clearRuntimeCache = function () {
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache.prototype._getPkgRelease = function (pkgMeta) {
|
||||
var release = pkgMeta.version || (pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
|
||||
ResolveCache.prototype._getPkgRelease = function(pkgMeta) {
|
||||
var release =
|
||||
pkgMeta.version ||
|
||||
(pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
|
||||
|
||||
// Encode some dangerous chars such as / and \
|
||||
release = encodeURIComponent(release);
|
||||
@@ -333,16 +366,15 @@ ResolveCache.prototype._getPkgRelease = function (pkgMeta) {
|
||||
return release;
|
||||
};
|
||||
|
||||
ResolveCache.prototype._readPkgMeta = function (dir) {
|
||||
ResolveCache.prototype._readPkgMeta = function(dir) {
|
||||
var filename = path.join(dir, '.bower.json');
|
||||
|
||||
return readJson(filename)
|
||||
.spread(function (json) {
|
||||
return readJson(filename).spread(function(json) {
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype._getVersions = function (sourceId) {
|
||||
ResolveCache.prototype._getVersions = function(sourceId) {
|
||||
var dir;
|
||||
var versions = this._cache.get(sourceId);
|
||||
var that = this;
|
||||
@@ -352,29 +384,31 @@ ResolveCache.prototype._getVersions = function (sourceId) {
|
||||
}
|
||||
|
||||
dir = path.join(this._dir, sourceId);
|
||||
return Q.nfcall(fs.readdir, dir)
|
||||
.then(function (versions) {
|
||||
// Sort and cache in memory
|
||||
that._sortVersions(versions);
|
||||
versions = versions.map(decodeURIComponent);
|
||||
that._cache.set(sourceId, versions);
|
||||
return [versions, false];
|
||||
}, function (err) {
|
||||
// If the directory does not exists, resolve
|
||||
// as an empty array
|
||||
if (err.code === 'ENOENT') {
|
||||
versions = [];
|
||||
return Q.nfcall(fs.readdir, dir).then(
|
||||
function(versions) {
|
||||
// Sort and cache in memory
|
||||
that._sortVersions(versions);
|
||||
versions = versions.map(decodeURIComponent);
|
||||
that._cache.set(sourceId, versions);
|
||||
return [versions, false];
|
||||
}
|
||||
},
|
||||
function(err) {
|
||||
// If the directory does not exists, resolve
|
||||
// as an empty array
|
||||
if (err.code === 'ENOENT') {
|
||||
versions = [];
|
||||
that._cache.set(sourceId, versions);
|
||||
return [versions, false];
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
ResolveCache.prototype._sortVersions = function (versions) {
|
||||
ResolveCache.prototype._sortVersions = function(versions) {
|
||||
// Sort DESC
|
||||
versions.sort(function (version1, version2) {
|
||||
versions.sort(function(version1, version2) {
|
||||
var validSemver1 = semver.valid(version1);
|
||||
var validSemver2 = semver.valid(version2);
|
||||
|
||||
@@ -400,7 +434,7 @@ ResolveCache.prototype._sortVersions = function (versions) {
|
||||
|
||||
ResolveCache._cache = new LRU({
|
||||
max: 5,
|
||||
maxAge: 60 * 30 * 1000 // 30 minutes
|
||||
maxAge: 60 * 30 * 1000 // 30 minutes
|
||||
});
|
||||
|
||||
module.exports = ResolveCache;
|
||||
|
||||
@@ -13,9 +13,15 @@ function createInstance(decEndpoint, options, registryClient) {
|
||||
|
||||
options.version = require('../version');
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient)
|
||||
.spread(function (ConcreteResolver, decEndpoint) {
|
||||
return new ConcreteResolver(decEndpoint, options.config, options.logger);
|
||||
return getConstructor(decEndpoint, options, registryClient).spread(function(
|
||||
ConcreteResolver,
|
||||
decEndpoint
|
||||
) {
|
||||
return new ConcreteResolver(
|
||||
decEndpoint,
|
||||
options.config,
|
||||
options.logger
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,8 +35,8 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
|
||||
var promise = Q.resolve();
|
||||
|
||||
var addResolver = function (resolverFactory) {
|
||||
promise = promise.then(function (result) {
|
||||
var addResolver = function(resolverFactory) {
|
||||
promise = promise.then(function(result) {
|
||||
if (result === undefined) {
|
||||
return resolverFactory(decEndpoint, options);
|
||||
} else {
|
||||
@@ -43,7 +49,7 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
//
|
||||
// It requires each resolver defined in config.resolvers and calls
|
||||
// its "match" to check if given resolves supports given decEndpoint
|
||||
addResolver(function () {
|
||||
addResolver(function() {
|
||||
var selectedResolver;
|
||||
var resolverNames;
|
||||
|
||||
@@ -55,28 +61,32 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
resolverNames = [];
|
||||
}
|
||||
|
||||
var resolverPromises = resolverNames.map(function (resolverName) {
|
||||
|
||||
|
||||
var resolverPromises = resolverNames.map(function(resolverName) {
|
||||
var resolver = resolvers[resolverName];
|
||||
|
||||
if (resolver === undefined) {
|
||||
var resolverPath = resolve(resolverName, { cwd: config.cwd });
|
||||
|
||||
if (resolverPath === undefined) {
|
||||
throw createError('Bower resolver not found: ' + resolverName, 'ENORESOLVER')
|
||||
throw createError(
|
||||
'Bower resolver not found: ' + resolverName,
|
||||
'ENORESOLVER'
|
||||
);
|
||||
}
|
||||
|
||||
resolver = pluginResolverFactory(require(resolverPath), options);
|
||||
resolver = pluginResolverFactory(
|
||||
require(resolverPath),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
return function () {
|
||||
return function() {
|
||||
if (selectedResolver === undefined) {
|
||||
var match = resolver.match.bind(resolver);
|
||||
|
||||
return Q.fcall(match, source).then(function (result) {
|
||||
return Q.fcall(match, source).then(function(result) {
|
||||
if (result) {
|
||||
return selectedResolver = resolver;
|
||||
return (selectedResolver = resolver);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -85,26 +95,39 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
};
|
||||
});
|
||||
|
||||
return resolverPromises.reduce(Q.when, new Q(undefined)).then(function (resolver) {
|
||||
if (resolver) {
|
||||
return Q.fcall(resolver.locate.bind(resolver), decEndpoint.source).then(function (result) {
|
||||
if (result && result !== decEndpoint.source) {
|
||||
decEndpoint.source = result;
|
||||
decEndpoint.registry = true;
|
||||
return getConstructor(decEndpoint, options, registryClient);
|
||||
} else {
|
||||
return [resolver, decEndpoint];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return resolverPromises
|
||||
.reduce(Q.when, new Q(undefined))
|
||||
.then(function(resolver) {
|
||||
if (resolver) {
|
||||
return Q.fcall(
|
||||
resolver.locate.bind(resolver),
|
||||
decEndpoint.source
|
||||
).then(function(result) {
|
||||
if (result && result !== decEndpoint.source) {
|
||||
decEndpoint.source = result;
|
||||
decEndpoint.registry = true;
|
||||
return getConstructor(
|
||||
decEndpoint,
|
||||
options,
|
||||
registryClient
|
||||
);
|
||||
} else {
|
||||
return [resolver, decEndpoint];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Git case: git git+ssh, git+http, git+https
|
||||
// .git at the end (probably ssh shorthand)
|
||||
// git@ at the start
|
||||
addResolver(function () {
|
||||
if (/^git(\+(ssh|https?))?:\/\//i.test(source) || /\.git\/?$/i.test(source) || /^git@/i.test(source)) {
|
||||
addResolver(function() {
|
||||
if (
|
||||
/^git(\+(ssh|https?))?:\/\//i.test(source) ||
|
||||
/\.git\/?$/i.test(source) ||
|
||||
/^git@/i.test(source)
|
||||
) {
|
||||
decEndpoint.source = source.replace(/^git\+/, '');
|
||||
|
||||
// If it's a GitHub repository, return the specialized resolver
|
||||
@@ -117,65 +140,74 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
});
|
||||
|
||||
// SVN case: svn, svn+ssh, svn+http, svn+https, svn+file
|
||||
addResolver(function () {
|
||||
addResolver(function() {
|
||||
if (/^svn(\+(ssh|https?|file))?:\/\//i.test(source)) {
|
||||
return [resolvers.Svn, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
// URL case
|
||||
addResolver(function () {
|
||||
addResolver(function() {
|
||||
if (/^https?:\/\//i.exec(source)) {
|
||||
return [resolvers.Url, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// If source is ./ or ../ or an absolute path
|
||||
|
||||
addResolver(function () {
|
||||
addResolver(function() {
|
||||
var absolutePath = path.resolve(config.cwd, source);
|
||||
|
||||
if (/^\.\.?[\/\\]/.test(source) || /^~\//.test(source) ||
|
||||
if (
|
||||
/^\.\.?[\/\\]/.test(source) ||
|
||||
/^~\//.test(source) ||
|
||||
path.normalize(source).replace(/[\/\\]+$/, '') === absolutePath
|
||||
) {
|
||||
return Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
|
||||
.then(function (stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
return (
|
||||
Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
|
||||
.then(function(stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.GitFs, decEndpoint]);
|
||||
}
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.GitFs, decEndpoint]);
|
||||
}
|
||||
|
||||
throw new Error('Not a Git repository');
|
||||
})
|
||||
// If not, check if source is a valid Subversion repository
|
||||
.fail(function () {
|
||||
return Q.nfcall(fs.stat, path.join(absolutePath, '.svn'))
|
||||
.then(function (stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
throw new Error('Not a Git repository');
|
||||
})
|
||||
// If not, check if source is a valid Subversion repository
|
||||
.fail(function() {
|
||||
return Q.nfcall(
|
||||
fs.stat,
|
||||
path.join(absolutePath, '.svn')
|
||||
).then(function(stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.Svn, decEndpoint]);
|
||||
}
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.Svn, decEndpoint]);
|
||||
}
|
||||
|
||||
throw new Error('Not a Subversion repository');
|
||||
});
|
||||
})
|
||||
// If not, check if source is a valid file/folder
|
||||
.fail(function () {
|
||||
return Q.nfcall(fs.stat, absolutePath)
|
||||
.then(function () {
|
||||
decEndpoint.source = absolutePath;
|
||||
throw new Error('Not a Subversion repository');
|
||||
});
|
||||
})
|
||||
// If not, check if source is a valid file/folder
|
||||
.fail(function() {
|
||||
return Q.nfcall(fs.stat, absolutePath).then(function() {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
return Q.resolve([resolvers.Fs, decEndpoint]);
|
||||
});
|
||||
});
|
||||
return Q.resolve([resolvers.Fs, decEndpoint]);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if is a shorthand and expand it
|
||||
addResolver(function () {
|
||||
addResolver(function() {
|
||||
// Check if the shorthandResolver is falsy
|
||||
if (!config.shorthandResolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip ssh and/or URL with auth
|
||||
if (/[:@]/.test(source)) {
|
||||
return;
|
||||
@@ -184,26 +216,34 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
// Ensure exactly only one "/"
|
||||
var parts = source.split('/');
|
||||
if (parts.length === 2) {
|
||||
decEndpoint.source = mout.string.interpolate(config.shorthandResolver, {
|
||||
shorthand: source,
|
||||
owner: parts[0],
|
||||
package: parts[1]
|
||||
});
|
||||
decEndpoint.source = mout.string.interpolate(
|
||||
config.shorthandResolver,
|
||||
{
|
||||
shorthand: source,
|
||||
owner: parts[0],
|
||||
package: parts[1]
|
||||
}
|
||||
);
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient);
|
||||
}
|
||||
});
|
||||
|
||||
// As last resort, we try the registry
|
||||
addResolver(function () {
|
||||
addResolver(function() {
|
||||
if (!registryClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), source)
|
||||
.then(function (entry) {
|
||||
return Q.nfcall(
|
||||
registryClient.lookup.bind(registryClient),
|
||||
source
|
||||
).then(function(entry) {
|
||||
if (!entry) {
|
||||
throw createError('Package ' + source + ' not found', 'ENOTFOUND');
|
||||
throw createError(
|
||||
'Package ' + source + ' not found',
|
||||
'ENOTFOUND'
|
||||
);
|
||||
}
|
||||
|
||||
decEndpoint.registry = true;
|
||||
@@ -218,15 +258,18 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
});
|
||||
});
|
||||
|
||||
addResolver(function () {
|
||||
throw createError('Could not find appropriate resolver for ' + source, 'ENORESOLVER');
|
||||
addResolver(function() {
|
||||
throw createError(
|
||||
'Could not find appropriate resolver for ' + source,
|
||||
'ENORESOLVER'
|
||||
);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function clearRuntimeCache() {
|
||||
mout.object.values(resolvers).forEach(function (ConcreteResolver) {
|
||||
mout.object.values(resolvers).forEach(function(ConcreteResolver) {
|
||||
ConcreteResolver.clearRuntimeCache();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,13 +17,19 @@ function FsResolver(decEndpoint, config, logger) {
|
||||
|
||||
// If target was specified, simply reject the promise
|
||||
if (this._target !== '*') {
|
||||
throw createError('File system sources can\'t resolve targets', 'ENORESTARGET');
|
||||
throw createError(
|
||||
"File system sources can't resolve targets",
|
||||
'ENORESTARGET'
|
||||
);
|
||||
}
|
||||
|
||||
// If the name was guessed
|
||||
if (this._guessedName) {
|
||||
// Remove extension
|
||||
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
|
||||
this._name = this._name.substr(
|
||||
0,
|
||||
this._name.length - path.extname(this._name).length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +38,7 @@ mout.object.mixIn(FsResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
FsResolver.isTargetable = function () {
|
||||
FsResolver.isTargetable = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -42,19 +48,18 @@ FsResolver.isTargetable = function () {
|
||||
// TODO: There's room for improvement by using streams if the source
|
||||
// is an archive file, by piping read stream to the zip extractor
|
||||
// This will likely increase the complexity of code but might worth it
|
||||
FsResolver.prototype._resolve = function () {
|
||||
FsResolver.prototype._resolve = function() {
|
||||
return this._copy()
|
||||
.then(this._extract.bind(this))
|
||||
.then(this._rename.bind(this));
|
||||
.then(this._extract.bind(this))
|
||||
.then(this._rename.bind(this));
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
FsResolver.prototype._copy = function () {
|
||||
FsResolver.prototype._copy = function() {
|
||||
var that = this;
|
||||
|
||||
return Q.nfcall(fs.stat, this._source)
|
||||
.then(function (stat) {
|
||||
return Q.nfcall(fs.stat, this._source).then(function(stat) {
|
||||
var dst;
|
||||
var copyOpts;
|
||||
var promise;
|
||||
@@ -68,22 +73,24 @@ FsResolver.prototype._copy = function () {
|
||||
|
||||
// Read the bower.json inside the folder, so that we
|
||||
// copy only the necessary files if it has ignore specified
|
||||
promise = that._readJson(that._source)
|
||||
.then(function (json) {
|
||||
copyOpts.ignore = json.ignore;
|
||||
return copy.copyDir(that._source, dst, copyOpts);
|
||||
})
|
||||
.then(function () {
|
||||
// Resolve to null because it's a dir
|
||||
return;
|
||||
});
|
||||
// Else it's a file
|
||||
promise = that
|
||||
._readJson(that._source)
|
||||
.then(function(json) {
|
||||
copyOpts.ignore = json.ignore;
|
||||
return copy.copyDir(that._source, dst, copyOpts);
|
||||
})
|
||||
.then(function() {
|
||||
// Resolve to null because it's a dir
|
||||
return;
|
||||
});
|
||||
// Else it's a file
|
||||
} else {
|
||||
dst = path.join(that._tempDir, path.basename(that._source));
|
||||
promise = copy.copyFile(that._source, dst, copyOpts)
|
||||
.then(function () {
|
||||
return dst;
|
||||
});
|
||||
promise = copy
|
||||
.copyFile(that._source, dst, copyOpts)
|
||||
.then(function() {
|
||||
return dst;
|
||||
});
|
||||
}
|
||||
|
||||
that._logger.action('copy', that._source, {
|
||||
@@ -95,7 +102,7 @@ FsResolver.prototype._copy = function () {
|
||||
});
|
||||
};
|
||||
|
||||
FsResolver.prototype._extract = function (file) {
|
||||
FsResolver.prototype._extract = function(file) {
|
||||
if (!file || !extract.canExtract(file)) {
|
||||
return Q.resolve();
|
||||
}
|
||||
@@ -108,30 +115,34 @@ FsResolver.prototype._extract = function (file) {
|
||||
return extract(file, this._tempDir);
|
||||
};
|
||||
|
||||
FsResolver.prototype._rename = function () {
|
||||
return Q.nfcall(fs.readdir, this._tempDir)
|
||||
.then(function (files) {
|
||||
var file;
|
||||
var oldPath;
|
||||
var newPath;
|
||||
FsResolver.prototype._rename = function() {
|
||||
return Q.nfcall(fs.readdir, this._tempDir).then(
|
||||
function(files) {
|
||||
var file;
|
||||
var oldPath;
|
||||
var newPath;
|
||||
|
||||
// Remove any OS specific files from the files array
|
||||
// before checking its length
|
||||
files = files.filter(junk.isnt);
|
||||
// Remove any OS specific files from the files array
|
||||
// before checking its length
|
||||
files = files.filter(junk.isnt);
|
||||
|
||||
// Only rename if there's only one file and it's not the json
|
||||
if (files.length === 1 && !/^(bower|component)\.json$/.test(files[0])) {
|
||||
file = files[0];
|
||||
this._singleFile = 'index' + path.extname(file);
|
||||
oldPath = path.join(this._tempDir, file);
|
||||
newPath = path.join(this._tempDir, this._singleFile);
|
||||
// Only rename if there's only one file and it's not the json
|
||||
if (
|
||||
files.length === 1 &&
|
||||
!/^(bower|component)\.json$/.test(files[0])
|
||||
) {
|
||||
file = files[0];
|
||||
this._singleFile = 'index' + path.extname(file);
|
||||
oldPath = path.join(this._tempDir, file);
|
||||
newPath = path.join(this._tempDir, this._singleFile);
|
||||
|
||||
return Q.nfcall(fs.rename, oldPath, newPath);
|
||||
}
|
||||
}.bind(this));
|
||||
return Q.nfcall(fs.rename, oldPath, newPath);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
FsResolver.prototype._savePkgMeta = function (meta) {
|
||||
FsResolver.prototype._savePkgMeta = function(meta) {
|
||||
// Store main if is a single file
|
||||
if (this._singleFile) {
|
||||
meta.main = this._singleFile;
|
||||
|
||||
@@ -19,31 +19,52 @@ mout.object.mixIn(GitFsResolver, GitResolver);
|
||||
// -----------------
|
||||
|
||||
// Override the checkout function to work with the local copy
|
||||
GitFsResolver.prototype._checkout = function () {
|
||||
GitFsResolver.prototype._checkout = function() {
|
||||
var resolution = this._resolution;
|
||||
|
||||
// The checkout process could be similar to the GitRemoteResolver by prepending file:// to the source
|
||||
// But from my performance measures, it's faster to copy the folder and just checkout in there
|
||||
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
});
|
||||
this._logger.action(
|
||||
'checkout',
|
||||
resolution.tag || resolution.branch || resolution.commit,
|
||||
{
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
}
|
||||
);
|
||||
|
||||
// Copy files to the temporary directory first
|
||||
return this._copy()
|
||||
.then(cmd.bind(cmd, 'git', ['checkout', '-f', resolution.tag || resolution.branch || resolution.commit], { cwd: this._tempDir }))
|
||||
// Cleanup unstaged files
|
||||
.then(cmd.bind(cmd, 'git', ['clean', '-f', '-d'], { cwd: this._tempDir }));
|
||||
return (
|
||||
this._copy()
|
||||
.then(
|
||||
cmd.bind(
|
||||
cmd,
|
||||
'git',
|
||||
[
|
||||
'checkout',
|
||||
'-f',
|
||||
resolution.tag || resolution.branch || resolution.commit
|
||||
],
|
||||
{ cwd: this._tempDir }
|
||||
)
|
||||
)
|
||||
// Cleanup unstaged files
|
||||
.then(
|
||||
cmd.bind(cmd, 'git', ['clean', '-f', '-d'], {
|
||||
cwd: this._tempDir
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
GitFsResolver.prototype._copy = function () {
|
||||
GitFsResolver.prototype._copy = function() {
|
||||
return copy.copyDir(this._source, this._tempDir);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Grab refs locally
|
||||
GitFsResolver.refs = function (source) {
|
||||
GitFsResolver.refs = function(source) {
|
||||
var value;
|
||||
|
||||
// TODO: Normalize source because of the various available protocols?
|
||||
@@ -52,20 +73,24 @@ GitFsResolver.refs = function (source) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('git', ['show-ref', '--tags', '--heads'], { cwd : source })
|
||||
.spread(function (stdout) {
|
||||
var refs;
|
||||
value = cmd('git', ['show-ref', '--tags', '--heads'], {
|
||||
cwd: source
|
||||
}).spread(
|
||||
function(stdout) {
|
||||
var refs;
|
||||
|
||||
refs = stdout.toString()
|
||||
.trim() // Trim trailing and leading spaces
|
||||
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
|
||||
.split(/[\r\n]+/); // Split lines into an array
|
||||
refs = stdout
|
||||
.toString()
|
||||
.trim() // Trim trailing and leading spaces
|
||||
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
|
||||
.split(/[\r\n]+/); // Split lines into an array
|
||||
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
|
||||
return refs;
|
||||
}.bind(this));
|
||||
return refs;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
|
||||
@@ -29,16 +29,13 @@ function GitHubResolver(decEndpoint, config, logger) {
|
||||
this._source += '.git';
|
||||
}
|
||||
|
||||
// Check if it's public
|
||||
this._public = mout.string.startsWith(this._source, 'git://');
|
||||
|
||||
// Use https:// rather than git:// if on a proxy
|
||||
if (this._config.proxy || this._config.httpsProxy) {
|
||||
this._source = this._source.replace('git://', 'https://');
|
||||
}
|
||||
|
||||
// Enable shallow clones for GitHub repos
|
||||
this._shallowClone = function () {
|
||||
this._shallowClone = function() {
|
||||
return Q.resolve(true);
|
||||
};
|
||||
}
|
||||
@@ -48,15 +45,21 @@ mout.object.mixIn(GitHubResolver, GitRemoteResolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitHubResolver.prototype._checkout = function () {
|
||||
// Only fully works with public repositories and tags
|
||||
// Could work with https/ssh protocol but not with 100% certainty
|
||||
if (!this._public || !this._resolution.tag) {
|
||||
return GitRemoteResolver.prototype._checkout.call(this);
|
||||
}
|
||||
|
||||
GitHubResolver.prototype._checkout = function() {
|
||||
var msg;
|
||||
var tarballUrl = 'https://github.com/' + this._org + '/' + this._repo + '/archive/' + this._resolution.tag + '.tar.gz';
|
||||
var name =
|
||||
this._resolution.tag ||
|
||||
this._resolution.branch ||
|
||||
this._resolution.commit;
|
||||
var tarballUrl =
|
||||
'https://github.com/' +
|
||||
this._org +
|
||||
'/' +
|
||||
this._repo +
|
||||
'/archive/' +
|
||||
name +
|
||||
'.tar.gz';
|
||||
|
||||
var file = path.join(this._tempDir, 'archive.tar.gz');
|
||||
var reqHeaders = {};
|
||||
var that = this;
|
||||
@@ -77,55 +80,88 @@ GitHubResolver.prototype._checkout = function () {
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
})
|
||||
.progress(function (state) {
|
||||
// Retry?
|
||||
if (state.retry) {
|
||||
msg = 'Download of ' + tarballUrl + ' failed with ' + state.error.code + ', ';
|
||||
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.debug('error', state.error.message, { error: state.error });
|
||||
return that._logger.warn('retry', msg);
|
||||
}
|
||||
.progress(function(state) {
|
||||
// Retry?
|
||||
if (state.retry) {
|
||||
msg =
|
||||
'Download of ' +
|
||||
tarballUrl +
|
||||
' failed with ' +
|
||||
state.error.code +
|
||||
', ';
|
||||
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.debug('error', state.error.message, {
|
||||
error: state.error
|
||||
});
|
||||
return that._logger.warn('retry', msg);
|
||||
}
|
||||
|
||||
// Progress
|
||||
msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
|
||||
if (state.total) {
|
||||
msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
|
||||
msg += state.percent + '%';
|
||||
}
|
||||
that._logger.info('progress', msg);
|
||||
})
|
||||
.then(function () {
|
||||
// Extract archive
|
||||
that._logger.action('extract', path.basename(file), {
|
||||
archive: file,
|
||||
to: that._tempDir
|
||||
});
|
||||
// Progress
|
||||
msg =
|
||||
'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
|
||||
if (state.total) {
|
||||
msg +=
|
||||
' of ' +
|
||||
(state.total / 1024 / 1024).toFixed(1) +
|
||||
'MB downloaded, ';
|
||||
msg += state.percent + '%';
|
||||
}
|
||||
that._logger.info('progress', msg);
|
||||
})
|
||||
.then(
|
||||
function() {
|
||||
// Extract archive
|
||||
that._logger.action('extract', path.basename(file), {
|
||||
archive: file,
|
||||
to: that._tempDir
|
||||
});
|
||||
|
||||
return extract(file, that._tempDir)
|
||||
// Fallback to standard git clone if extraction failed
|
||||
.fail(function (err) {
|
||||
msg = 'Decompression of ' + path.basename(file) + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
|
||||
msg += 'trying with git..';
|
||||
that._logger.debug('error', err.message, { error: err });
|
||||
that._logger.warn('retry', msg);
|
||||
return (
|
||||
extract(file, that._tempDir)
|
||||
// Fallback to standard git clone if extraction failed
|
||||
.fail(function(err) {
|
||||
msg =
|
||||
'Decompression of ' +
|
||||
path.basename(file) +
|
||||
' failed' +
|
||||
(err.code ? ' with ' + err.code : '') +
|
||||
', ';
|
||||
msg += 'trying with git..';
|
||||
that._logger.debug('error', err.message, {
|
||||
error: err
|
||||
});
|
||||
that._logger.warn('retry', msg);
|
||||
|
||||
return that._cleanTempDir()
|
||||
.then(GitRemoteResolver.prototype._checkout.bind(that));
|
||||
});
|
||||
// Fallback to standard git clone if download failed
|
||||
}, function (err) {
|
||||
msg = 'Download of ' + tarballUrl + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
|
||||
msg += 'trying with git..';
|
||||
that._logger.debug('error', err.message, { error: err });
|
||||
that._logger.warn('retry', msg);
|
||||
return that
|
||||
._cleanTempDir()
|
||||
.then(
|
||||
GitRemoteResolver.prototype._checkout.bind(
|
||||
that
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
// Fallback to standard git clone if download failed
|
||||
},
|
||||
function(err) {
|
||||
msg =
|
||||
'Download of ' +
|
||||
tarballUrl +
|
||||
' failed' +
|
||||
(err.code ? ' with ' + err.code : '') +
|
||||
', ';
|
||||
msg += 'trying with git..';
|
||||
that._logger.debug('error', err.message, { error: err });
|
||||
that._logger.warn('retry', msg);
|
||||
|
||||
return that._cleanTempDir()
|
||||
.then(GitRemoteResolver.prototype._checkout.bind(that));
|
||||
|
||||
});
|
||||
return that
|
||||
._cleanTempDir()
|
||||
.then(GitRemoteResolver.prototype._checkout.bind(that));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
GitHubResolver.prototype._savePkgMeta = function (meta) {
|
||||
GitHubResolver.prototype._savePkgMeta = function(meta) {
|
||||
// Set homepage if not defined
|
||||
if (!meta.homepage) {
|
||||
meta.homepage = 'https://github.com/' + this._org + '/' + this._repo;
|
||||
@@ -136,10 +172,12 @@ GitHubResolver.prototype._savePkgMeta = function (meta) {
|
||||
|
||||
// ----------------
|
||||
|
||||
GitHubResolver.getOrgRepoPair = function (url) {
|
||||
GitHubResolver.getOrgRepoPair = function(url) {
|
||||
var match;
|
||||
|
||||
match = url.match(/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i);
|
||||
match = url.match(
|
||||
/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i
|
||||
);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@ function GitRemoteResolver(decEndpoint, config, logger) {
|
||||
this._name = this._name.slice(0, -4);
|
||||
}
|
||||
|
||||
// Get the host of this source
|
||||
// Get the remote of this source
|
||||
if (!/:\/\//.test(this._source)) {
|
||||
this._host = url.parse('ssh://' + this._source).host;
|
||||
this._remote = url.parse('ssh://' + this._source);
|
||||
} else {
|
||||
this._host = url.parse(this._source).host;
|
||||
this._remote = url.parse(this._source);
|
||||
}
|
||||
|
||||
this._remote = url.parse(this._source);
|
||||
this._host = this._remote.host;
|
||||
|
||||
// Verify whether the server supports shallow cloning
|
||||
this._shallowClone = this._supportsShallowCloning;
|
||||
@@ -37,33 +37,37 @@ mout.object.mixIn(GitRemoteResolver, GitResolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitRemoteResolver.prototype._checkout = function () {
|
||||
GitRemoteResolver.prototype._checkout = function() {
|
||||
var promise;
|
||||
var timer;
|
||||
var reporter;
|
||||
var that = this;
|
||||
var resolution = this._resolution;
|
||||
|
||||
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
});
|
||||
this._logger.action(
|
||||
'checkout',
|
||||
resolution.tag || resolution.branch || resolution.commit,
|
||||
{
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
}
|
||||
);
|
||||
|
||||
// If resolution is a commit, we need to clone the entire repo and check it out
|
||||
// Because a commit is not a named ref, there's no better solution
|
||||
if (resolution.type === 'commit') {
|
||||
promise = this._slowClone(resolution);
|
||||
// Otherwise we are checking out a named ref so we can optimize it
|
||||
// Otherwise we are checking out a named ref so we can optimize it
|
||||
} else {
|
||||
promise = this._fastClone(resolution);
|
||||
}
|
||||
|
||||
// Throttle the progress reporter to 1 time each sec
|
||||
reporter = mout.fn.throttle(function (data) {
|
||||
reporter = mout.fn.throttle(function(data) {
|
||||
var lines;
|
||||
|
||||
lines = data.split(/[\r\n]+/);
|
||||
lines.forEach(function (line) {
|
||||
lines.forEach(function(line) {
|
||||
if (/\d{1,3}\%/.test(line)) {
|
||||
// TODO: There are some strange chars that appear once in a while (\u001b[K)
|
||||
// Trim also those?
|
||||
@@ -73,94 +77,125 @@ GitRemoteResolver.prototype._checkout = function () {
|
||||
}, 1000);
|
||||
|
||||
// Start reporting progress after a few seconds
|
||||
timer = setTimeout(function () {
|
||||
timer = setTimeout(function() {
|
||||
promise.progress(reporter);
|
||||
}, 8000);
|
||||
|
||||
return promise
|
||||
// Add additional proxy information to the error if necessary
|
||||
.fail(function (err) {
|
||||
that._suggestProxyWorkaround(err);
|
||||
throw err;
|
||||
})
|
||||
// Clear timer at the end
|
||||
.fin(function () {
|
||||
clearTimeout(timer);
|
||||
reporter.cancel();
|
||||
});
|
||||
return (
|
||||
promise
|
||||
// Add additional proxy information to the error if necessary
|
||||
.fail(function(err) {
|
||||
that._suggestProxyWorkaround(err);
|
||||
throw err;
|
||||
})
|
||||
// Clear timer at the end
|
||||
.fin(function() {
|
||||
clearTimeout(timer);
|
||||
reporter.cancel();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
GitRemoteResolver.prototype._findResolution = function (target) {
|
||||
GitRemoteResolver.prototype._findResolution = function(target) {
|
||||
var that = this;
|
||||
|
||||
// Override this function to include a meaningful message related to proxies
|
||||
// if necessary
|
||||
return GitResolver.prototype._findResolution.call(this, target)
|
||||
.fail(function (err) {
|
||||
that._suggestProxyWorkaround(err);
|
||||
throw err;
|
||||
});
|
||||
return GitResolver.prototype._findResolution
|
||||
.call(this, target)
|
||||
.fail(function(err) {
|
||||
that._suggestProxyWorkaround(err);
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
GitRemoteResolver.prototype._slowClone = function (resolution) {
|
||||
return cmd('git', ['clone', this._source, this._tempDir, '--progress'])
|
||||
.then(cmd.bind(cmd, 'git', ['checkout', resolution.commit], { cwd: this._tempDir }));
|
||||
GitRemoteResolver.prototype._slowClone = function(resolution) {
|
||||
return cmd('git', [
|
||||
'clone',
|
||||
this._source,
|
||||
this._tempDir,
|
||||
'--progress'
|
||||
]).then(
|
||||
cmd.bind(cmd, 'git', ['checkout', resolution.commit], {
|
||||
cwd: this._tempDir
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
GitRemoteResolver.prototype._fastClone = function (resolution) {
|
||||
GitRemoteResolver.prototype._fastClone = function(resolution) {
|
||||
var branch,
|
||||
args,
|
||||
that = this;
|
||||
|
||||
branch = resolution.tag || resolution.branch;
|
||||
args = ['clone', this._source, '-b', branch, '--progress', '.'];
|
||||
args = ['clone', this._source, '-b', branch, '--progress', '.'];
|
||||
|
||||
return this._shallowClone().then(function (shallowCloningSupported) {
|
||||
return this._shallowClone().then(function(shallowCloningSupported) {
|
||||
// If the host does not support shallow clones, we don't use --depth=1
|
||||
if (shallowCloningSupported && !GitRemoteResolver._noShallow.get(that._host)) {
|
||||
if (
|
||||
shallowCloningSupported &&
|
||||
!GitRemoteResolver._noShallow.get(that._host)
|
||||
) {
|
||||
args.push('--depth', 1);
|
||||
}
|
||||
|
||||
return cmd('git', args, { cwd: that._tempDir })
|
||||
.spread(function (stdout, stderr) {
|
||||
// Only after 1.7.10 --branch accepts tags
|
||||
// Detect those cases and inform the user to update git otherwise it's
|
||||
// a lot slower than newer versions
|
||||
if (!/branch .+? not found/i.test(stderr)) {
|
||||
return;
|
||||
}
|
||||
return cmd('git', args, { cwd: that._tempDir }).spread(
|
||||
function(stdout, stderr) {
|
||||
// Only after 1.7.10 --branch accepts tags
|
||||
// Detect those cases and inform the user to update git otherwise it's
|
||||
// a lot slower than newer versions
|
||||
if (!/branch .+? not found/i.test(stderr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
that._logger.warn('old-git', 'It seems you are using an old version of git, it will be slower and propitious to errors!');
|
||||
return cmd('git', ['checkout', resolution.commit], { cwd: that._tempDir });
|
||||
}, function (err) {
|
||||
// Some git servers do not support shallow clones
|
||||
// When that happens, we mark this host and try again
|
||||
if (!GitRemoteResolver._noShallow.has(that._source) &&
|
||||
err.details &&
|
||||
/(rpc failed|shallow|--depth)/i.test(err.details)
|
||||
that._logger.warn(
|
||||
'old-git',
|
||||
'It seems you are using an old version of git, it will be slower and propitious to errors!'
|
||||
);
|
||||
return cmd('git', ['checkout', resolution.commit], {
|
||||
cwd: that._tempDir
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
// Some git servers do not support shallow clones
|
||||
// When that happens, we mark this host and try again
|
||||
if (
|
||||
!GitRemoteResolver._noShallow.has(that._source) &&
|
||||
err.details &&
|
||||
/(rpc failed|shallow|--depth)/i.test(err.details)
|
||||
) {
|
||||
GitRemoteResolver._noShallow.set(that._host, true);
|
||||
return that._fastClone(resolution);
|
||||
}
|
||||
GitRemoteResolver._noShallow.set(that._host, true);
|
||||
return that._fastClone(resolution);
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
GitRemoteResolver.prototype._suggestProxyWorkaround = function (err) {
|
||||
if ((this._config.proxy || this._config.httpsProxy) &&
|
||||
GitRemoteResolver.prototype._suggestProxyWorkaround = function(err) {
|
||||
if (
|
||||
(this._config.proxy || this._config.httpsProxy) &&
|
||||
mout.string.startsWith(this._source, 'git://') &&
|
||||
err.code === 'ECMDERR' && err.details
|
||||
err.code === 'ECMDERR' &&
|
||||
err.details
|
||||
) {
|
||||
err.details = err.details.trim();
|
||||
err.details += '\n\nWhen under a proxy, you must configure git to use https:// instead of git://.';
|
||||
err.details += '\nYou can configure it for every endpoint or for this specific host as follows:';
|
||||
err.details +=
|
||||
'\n\nWhen under a proxy, you must configure git to use https:// instead of git://.';
|
||||
err.details +=
|
||||
'\nYou can configure it for every endpoint or for this specific host as follows:';
|
||||
err.details += '\ngit config --global url."https://".insteadOf git://';
|
||||
err.details += '\ngit config --global url."https://' + this._host + '".insteadOf git://' + this._host;
|
||||
err.details += 'Ignore this suggestion if you already have this configured.';
|
||||
err.details +=
|
||||
'\ngit config --global url."https://' +
|
||||
this._host +
|
||||
'".insteadOf git://' +
|
||||
this._host;
|
||||
err.details +=
|
||||
'Ignore this suggestion if you already have this configured.';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -182,7 +217,7 @@ GitRemoteResolver.prototype._suggestProxyWorkaround = function (err) {
|
||||
// negotiation that needs to take place.
|
||||
//
|
||||
// The above should cover most cases, including BitBucket.
|
||||
GitRemoteResolver.prototype._supportsShallowCloning = function () {
|
||||
GitRemoteResolver.prototype._supportsShallowCloning = function() {
|
||||
var value = true;
|
||||
|
||||
// Verify that the remote could be parsed and that a protocol is set
|
||||
@@ -191,8 +226,11 @@ GitRemoteResolver.prototype._supportsShallowCloning = function () {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
if (!this._host || !this._config.shallowCloneHosts || this._config.shallowCloneHosts.indexOf(this._host) === -1) {
|
||||
if (
|
||||
!this._host ||
|
||||
!this._config.shallowCloneHosts ||
|
||||
this._config.shallowCloneHosts.indexOf(this._host) === -1
|
||||
) {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
||||
@@ -200,34 +238,43 @@ GitRemoteResolver.prototype._supportsShallowCloning = function () {
|
||||
// HTTP or HTTPS, not for Git or SSH.
|
||||
// Also check for hosts that have been checked in a previous request and have been found to support
|
||||
// shallow cloning.
|
||||
if (mout.string.startsWith(this._remote.protocol, 'http')
|
||||
&& !GitRemoteResolver._canShallow.get(this._host)) {
|
||||
if (
|
||||
mout.string.startsWith(this._remote.protocol, 'http') &&
|
||||
!GitRemoteResolver._canShallow.get(this._host)
|
||||
) {
|
||||
// Provide GIT_CURL_VERBOSE=2 environment variable to capture curl output.
|
||||
// Calling ls-remote includes a call to the git-upload-pack service, which returns the content type in the response.
|
||||
var processEnv = mout.object.merge(process.env, { 'GIT_CURL_VERBOSE': '2' });
|
||||
var processEnv = mout.object.merge(process.env, {
|
||||
GIT_CURL_VERBOSE: '2'
|
||||
});
|
||||
|
||||
value = cmd('git', ['ls-remote', '--heads', this._source], {
|
||||
env: processEnv
|
||||
})
|
||||
.spread(function (stdout, stderr) {
|
||||
// Check stderr for content-type, ignore stdout
|
||||
var isSmartServer;
|
||||
}).spread(
|
||||
function(stdout, stderr) {
|
||||
// Check stderr for content-type, ignore stdout
|
||||
var isSmartServer;
|
||||
|
||||
// If the content type is 'x-git', then the server supports shallow cloning
|
||||
isSmartServer = mout.string.contains(stderr,
|
||||
'Content-Type: application/x-git-upload-pack-advertisement');
|
||||
// If the content type is 'x-git', then the server supports shallow cloning
|
||||
isSmartServer = mout.string.contains(
|
||||
stderr,
|
||||
'Content-Type: application/x-git-upload-pack-advertisement'
|
||||
);
|
||||
|
||||
this._logger.debug('detect-smart-git', 'Smart Git host detected: ' + isSmartServer);
|
||||
this._logger.debug(
|
||||
'detect-smart-git',
|
||||
'Smart Git host detected: ' + isSmartServer
|
||||
);
|
||||
|
||||
if (isSmartServer) {
|
||||
// Cache this host
|
||||
GitRemoteResolver._canShallow.set(this._host, true);
|
||||
}
|
||||
if (isSmartServer) {
|
||||
// Cache this host
|
||||
GitRemoteResolver._canShallow.set(this._host, true);
|
||||
}
|
||||
|
||||
return isSmartServer;
|
||||
}.bind(this));
|
||||
}
|
||||
else {
|
||||
return isSmartServer;
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
// One of the following cases:
|
||||
// * A non-HTTP/HTTPS protocol
|
||||
// * A host that has been checked before and that supports shallow cloning
|
||||
@@ -240,7 +287,7 @@ GitRemoteResolver.prototype._supportsShallowCloning = function () {
|
||||
// ------------------------------
|
||||
|
||||
// Grab refs remotely
|
||||
GitRemoteResolver.refs = function (source) {
|
||||
GitRemoteResolver.refs = function(source) {
|
||||
var value;
|
||||
|
||||
// TODO: Normalize source because of the various available protocols?
|
||||
@@ -250,20 +297,22 @@ GitRemoteResolver.refs = function (source) {
|
||||
}
|
||||
|
||||
// Store the promise in the refs object
|
||||
value = cmd('git', ['ls-remote', '--tags', '--heads', source])
|
||||
.spread(function (stdout) {
|
||||
var refs;
|
||||
value = cmd('git', ['ls-remote', '--tags', '--heads', source]).spread(
|
||||
function(stdout) {
|
||||
var refs;
|
||||
|
||||
refs = stdout.toString()
|
||||
.trim() // Trim trailing and leading spaces
|
||||
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
|
||||
.split(/[\r\n]+/); // Split lines into an array
|
||||
refs = stdout
|
||||
.toString()
|
||||
.trim() // Trim trailing and leading spaces
|
||||
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
|
||||
.split(/[\r\n]+/); // Split lines into an array
|
||||
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
|
||||
return refs;
|
||||
}.bind(this));
|
||||
return refs;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
|
||||
@@ -26,8 +26,18 @@ function GitResolver(decEndpoint, config, logger) {
|
||||
// anyway
|
||||
mkdirp.sync(config.storage.empty);
|
||||
process.env.GIT_TEMPLATE_DIR = config.storage.empty;
|
||||
process.env.GIT_SSL_NO_VERIFY = (!config.strictSsl).toString();
|
||||
process.env.GIT_TERMINAL_PROMPT = config.interactive ? '1' : '0';
|
||||
|
||||
if (!config.strictSsl) {
|
||||
process.env.GIT_SSL_NO_VERIFY = 'true';
|
||||
}
|
||||
|
||||
if (!config.interactive) {
|
||||
process.env.GIT_TERMINAL_PROMPT = '0';
|
||||
|
||||
if (!process.env.SSH_ASKPASS) {
|
||||
process.env.SSH_ASKPASS = 'echo';
|
||||
}
|
||||
}
|
||||
|
||||
Resolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
@@ -41,18 +51,20 @@ mout.object.mixIn(GitResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._hasNew = function (pkgMeta) {
|
||||
GitResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldResolution = pkgMeta._resolution || {};
|
||||
|
||||
return this._findResolution()
|
||||
.then(function (resolution) {
|
||||
return this._findResolution().then(function(resolution) {
|
||||
// Check if resolution types are different
|
||||
if (oldResolution.type !== resolution.type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If resolved to a version, there is new content if the tags are not equal
|
||||
if (resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag)) {
|
||||
if (
|
||||
resolution.type === 'version' &&
|
||||
semver.neq(resolution.tag, oldResolution.tag)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,35 +73,37 @@ GitResolver.prototype._hasNew = function (pkgMeta) {
|
||||
});
|
||||
};
|
||||
|
||||
GitResolver.prototype._resolve = function () {
|
||||
GitResolver.prototype._resolve = function() {
|
||||
var that = this;
|
||||
|
||||
return this._findResolution()
|
||||
.then(function () {
|
||||
return that._checkout()
|
||||
// Always run cleanup after checkout to ensure that .git is removed!
|
||||
// If it's not removed, problems might arise when the "tmp" module attempts
|
||||
// to delete the temporary folder
|
||||
.fin(function () {
|
||||
return that._cleanup();
|
||||
});
|
||||
return this._findResolution().then(function() {
|
||||
return (
|
||||
that
|
||||
._checkout()
|
||||
// Always run cleanup after checkout to ensure that .git is removed!
|
||||
// If it's not removed, problems might arise when the "tmp" module attempts
|
||||
// to delete the temporary folder
|
||||
.fin(function() {
|
||||
return that._cleanup();
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Abstract functions that should be implemented by concrete git resolvers
|
||||
GitResolver.prototype._checkout = function () {
|
||||
GitResolver.prototype._checkout = function() {
|
||||
throw new Error('_checkout not implemented');
|
||||
};
|
||||
|
||||
GitResolver.refs = function (source) {
|
||||
GitResolver.refs = function(source) {
|
||||
throw new Error('refs not implemented');
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._findResolution = function (target) {
|
||||
GitResolver.prototype._findResolution = function(target) {
|
||||
var err;
|
||||
var self = this.constructor;
|
||||
var that = this;
|
||||
@@ -98,20 +112,15 @@ GitResolver.prototype._findResolution = function (target) {
|
||||
|
||||
// Target is a commit, so it's a stale target (not a moving target)
|
||||
// There's nothing to do in this case
|
||||
if ((/^[a-f0-9]{40}$/).test(target)) {
|
||||
if (/^[a-f0-9]{40}$/.test(target)) {
|
||||
this._resolution = { type: 'commit', commit: target };
|
||||
return Q.resolve(this._resolution);
|
||||
}
|
||||
|
||||
// Target is a range/version
|
||||
if (semver.validRange(target)) {
|
||||
return self.versions(this._source, true)
|
||||
.then(function (versions) {
|
||||
var versionsArr,
|
||||
version,
|
||||
index;
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
return self.versions(this._source, true).then(function(versions) {
|
||||
var versionsArr, version, index;
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on master
|
||||
@@ -119,98 +128,143 @@ GitResolver.prototype._findResolution = function (target) {
|
||||
return that._findResolution('master');
|
||||
}
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
versionsArr = versions.map(function(obj) {
|
||||
return obj.version;
|
||||
});
|
||||
// Find a satisfying version, enabling strict match so that pre-releases
|
||||
// have lower priority over normal ones when target is *
|
||||
index = semver.maxSatisfyingIndex(versionsArr, target, true);
|
||||
if (index !== -1) {
|
||||
version = versions[index];
|
||||
return that._resolution = { type: 'version', tag: version.tag, commit: version.commit };
|
||||
return (that._resolution = {
|
||||
type: 'version',
|
||||
tag: version.tag,
|
||||
commit: version.commit
|
||||
});
|
||||
}
|
||||
|
||||
// Check if there's an exact branch/tag with this name as last resort
|
||||
return Q.all([
|
||||
self.branches(that._source),
|
||||
self.tags(that._source)
|
||||
])
|
||||
.spread(function (branches, tags) {
|
||||
]).spread(function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[target]
|
||||
});
|
||||
}
|
||||
|
||||
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
|
||||
details: !versions.length ?
|
||||
'No versions found in ' + that._source :
|
||||
'Available versions in ' + that._source + ': ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
});
|
||||
throw createError(
|
||||
'No tag found that was able to satisfy ' + target,
|
||||
'ENORESTARGET',
|
||||
{
|
||||
details: !versions.length
|
||||
? 'No versions found in ' + that._source
|
||||
: 'Available versions in ' +
|
||||
that._source +
|
||||
': ' +
|
||||
versions
|
||||
.map(function(version) {
|
||||
return version.version;
|
||||
})
|
||||
.join(', ')
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise, target is either a tag or a branch
|
||||
return Q.all([
|
||||
self.branches(that._source),
|
||||
self.tags(that._source)
|
||||
])
|
||||
.spread(function (branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
|
||||
}
|
||||
|
||||
if ((/^[a-f0-9]{4,40}$/).test(target)) {
|
||||
if (target.length < 12) {
|
||||
that._logger.warn(
|
||||
'short-sha',
|
||||
'Consider using longer commit SHA to avoid conflicts'
|
||||
);
|
||||
return Q.all([self.branches(that._source), self.tags(that._source)]).spread(
|
||||
function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[target]
|
||||
});
|
||||
}
|
||||
|
||||
that._resolution = { type: 'commit', commit: target };
|
||||
return that._resolution;
|
||||
if (/^[a-f0-9]{4,40}$/.test(target)) {
|
||||
if (target.length < 12) {
|
||||
that._logger.warn(
|
||||
'short-sha',
|
||||
'Consider using longer commit SHA to avoid conflicts'
|
||||
);
|
||||
}
|
||||
|
||||
that._resolution = { type: 'commit', commit: target };
|
||||
return that._resolution;
|
||||
}
|
||||
|
||||
branches = Object.keys(branches);
|
||||
tags = Object.keys(tags);
|
||||
|
||||
err = createError(
|
||||
'Tag/branch ' + target + ' does not exist',
|
||||
'ENORESTARGET'
|
||||
);
|
||||
err.details = !tags.length
|
||||
? 'No tags found in ' + that._source
|
||||
: 'Available tags: ' + tags.join(', ');
|
||||
err.details += '\n';
|
||||
err.details += !branches.length
|
||||
? 'No branches found in ' + that._source
|
||||
: 'Available branches: ' + branches.join(', ');
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
branches = Object.keys(branches);
|
||||
tags = Object.keys(tags);
|
||||
|
||||
err = createError('Tag/branch ' + target + ' does not exist', 'ENORESTARGET');
|
||||
err.details = !tags.length ?
|
||||
'No tags found in ' + that._source :
|
||||
'Available tags: ' + tags.join(', ');
|
||||
err.details += '\n';
|
||||
err.details += !branches.length ?
|
||||
'No branches found in ' + that._source :
|
||||
'Available branches: ' + branches.join(', ');
|
||||
|
||||
throw err;
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
GitResolver.prototype._cleanup = function () {
|
||||
GitResolver.prototype._cleanup = function() {
|
||||
var gitFolder = path.join(this._tempDir, '.git');
|
||||
|
||||
return Q.nfcall(rimraf, gitFolder);
|
||||
};
|
||||
|
||||
GitResolver.prototype._savePkgMeta = function (meta) {
|
||||
GitResolver.prototype._savePkgMeta = function(meta) {
|
||||
var version;
|
||||
|
||||
if (this._resolution.type === 'version') {
|
||||
version = semver.clean(this._resolution.tag);
|
||||
|
||||
// Warn if the package meta version is different than the resolved one
|
||||
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
|
||||
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
|
||||
resolution: this._resolution,
|
||||
pkgMeta: meta
|
||||
});
|
||||
if (
|
||||
typeof meta.version === 'string' &&
|
||||
semver.valid(meta.version) &&
|
||||
semver.neq(meta.version, version)
|
||||
) {
|
||||
this._logger.warn(
|
||||
'mismatch',
|
||||
'Version declared in the json (' +
|
||||
meta.version +
|
||||
') is different than the resolved one (' +
|
||||
version +
|
||||
')',
|
||||
{
|
||||
resolution: this._resolution,
|
||||
pkgMeta: meta
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure package meta version is the same as the resolution
|
||||
@@ -224,9 +278,10 @@ GitResolver.prototype._savePkgMeta = function (meta) {
|
||||
// Save version/tag/commit in the release
|
||||
// Note that we can't store branches because _release is supposed to be
|
||||
// an unique id of this ref.
|
||||
meta._release = version ||
|
||||
this._resolution.tag ||
|
||||
this._resolution.commit.substr(0, 10);
|
||||
meta._release =
|
||||
version ||
|
||||
this._resolution.tag ||
|
||||
this._resolution.commit.substr(0, 10);
|
||||
|
||||
// Save resolution to be used in hasNew later
|
||||
meta._resolution = this._resolution;
|
||||
@@ -236,51 +291,56 @@ GitResolver.prototype._savePkgMeta = function (meta) {
|
||||
|
||||
// ------------------------------
|
||||
|
||||
GitResolver.versions = function (source, extra) {
|
||||
GitResolver.versions = function(source, extra) {
|
||||
var value = this._cache.versions.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value)
|
||||
.then(function () {
|
||||
var versions = this._cache.versions.get(source);
|
||||
return Q.resolve(value).then(
|
||||
function() {
|
||||
var versions = this._cache.versions.get(source);
|
||||
|
||||
// If no extra information was requested,
|
||||
// resolve simply with the versions
|
||||
if (!extra) {
|
||||
versions = versions.map(function (version) {
|
||||
return version.version;
|
||||
});
|
||||
}
|
||||
// If no extra information was requested,
|
||||
// resolve simply with the versions
|
||||
if (!extra) {
|
||||
versions = versions.map(function(version) {
|
||||
return version.version;
|
||||
});
|
||||
}
|
||||
|
||||
return versions;
|
||||
}.bind(this));
|
||||
return versions;
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
value = this.tags(source)
|
||||
.then(function (tags) {
|
||||
var tag;
|
||||
var version;
|
||||
var versions = [];
|
||||
value = this.tags(source).then(
|
||||
function(tags) {
|
||||
var tag;
|
||||
var version;
|
||||
var versions = [];
|
||||
|
||||
// For each tag
|
||||
for (tag in tags) {
|
||||
version = semver.clean(tag);
|
||||
if (version) {
|
||||
versions.push({ version: version, tag: tag, commit: tags[tag] });
|
||||
// For each tag
|
||||
for (tag in tags) {
|
||||
version = semver.clean(tag);
|
||||
if (version) {
|
||||
versions.push({
|
||||
version: version,
|
||||
tag: tag,
|
||||
commit: tags[tag]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by DESC order
|
||||
versions.sort(function (a, b) {
|
||||
return semver.rcompare(a.version, b.version);
|
||||
});
|
||||
// Sort them by DESC order
|
||||
versions.sort(function(a, b) {
|
||||
return semver.rcompare(a.version, b.version);
|
||||
});
|
||||
|
||||
this._cache.versions.set(source, versions);
|
||||
|
||||
// Call the function again to keep it DRY
|
||||
return this.versions(source, extra);
|
||||
}.bind(this));
|
||||
this._cache.versions.set(source, versions);
|
||||
|
||||
// Call the function again to keep it DRY
|
||||
return this.versions(source, extra);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
@@ -289,30 +349,31 @@ GitResolver.versions = function (source, extra) {
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.tags = function (source) {
|
||||
GitResolver.tags = function(source) {
|
||||
var value = this._cache.tags.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = this.refs(source)
|
||||
.then(function (refs) {
|
||||
var tags = {};
|
||||
value = this.refs(source).then(
|
||||
function(refs) {
|
||||
var tags = {};
|
||||
|
||||
// For each line in the refs, match only the tags
|
||||
refs.forEach(function (line) {
|
||||
var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/);
|
||||
// For each line in the refs, match only the tags
|
||||
refs.forEach(function(line) {
|
||||
var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/);
|
||||
|
||||
if (match && !mout.string.endsWith(match[2], '^{}')) {
|
||||
tags[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
if (match && !mout.string.endsWith(match[2], '^{}')) {
|
||||
tags[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
|
||||
this._cache.tags.set(source, tags);
|
||||
this._cache.tags.set(source, tags);
|
||||
|
||||
return tags;
|
||||
}.bind(this));
|
||||
return tags;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
@@ -321,32 +382,33 @@ GitResolver.tags = function (source) {
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.branches = function (source) {
|
||||
GitResolver.branches = function(source) {
|
||||
var value = this._cache.branches.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = this.refs(source)
|
||||
.then(function (refs) {
|
||||
var branches = {};
|
||||
value = this.refs(source).then(
|
||||
function(refs) {
|
||||
var branches = {};
|
||||
|
||||
// For each line in the refs, extract only the heads
|
||||
// Organize them in an object where keys are branches and values
|
||||
// the commit hashes
|
||||
refs.forEach(function (line) {
|
||||
var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/);
|
||||
// For each line in the refs, extract only the heads
|
||||
// Organize them in an object where keys are branches and values
|
||||
// the commit hashes
|
||||
refs.forEach(function(line) {
|
||||
var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/);
|
||||
|
||||
if (match) {
|
||||
branches[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
if (match) {
|
||||
branches[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
|
||||
this._cache.branches.set(source, branches);
|
||||
this._cache.branches.set(source, branches);
|
||||
|
||||
return branches;
|
||||
}.bind(this));
|
||||
return branches;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
@@ -355,9 +417,9 @@ GitResolver.branches = function (source) {
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.clearRuntimeCache = function () {
|
||||
GitResolver.clearRuntimeCache = function() {
|
||||
// Reset cache for branches, tags, etc
|
||||
mout.object.forOwn(GitResolver._cache, function (lru) {
|
||||
mout.object.forOwn(GitResolver._cache, function(lru) {
|
||||
lru.reset();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -24,27 +24,27 @@ function Resolver(decEndpoint, config, logger) {
|
||||
|
||||
// -----------------
|
||||
|
||||
Resolver.prototype.getSource = function () {
|
||||
Resolver.prototype.getSource = function() {
|
||||
return this._source;
|
||||
};
|
||||
|
||||
Resolver.prototype.getName = function () {
|
||||
Resolver.prototype.getName = function() {
|
||||
return this._name;
|
||||
};
|
||||
|
||||
Resolver.prototype.getTarget = function () {
|
||||
Resolver.prototype.getTarget = function() {
|
||||
return this._target;
|
||||
};
|
||||
|
||||
Resolver.prototype.getTempDir = function () {
|
||||
Resolver.prototype.getTempDir = function() {
|
||||
return this._tempDir;
|
||||
};
|
||||
|
||||
Resolver.prototype.getPkgMeta = function () {
|
||||
Resolver.prototype.getPkgMeta = function() {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
Resolver.prototype.hasNew = function (pkgMeta) {
|
||||
Resolver.prototype.hasNew = function(pkgMeta) {
|
||||
var promise;
|
||||
var that = this;
|
||||
|
||||
@@ -58,12 +58,12 @@ Resolver.prototype.hasNew = function (pkgMeta) {
|
||||
// Avoid reading the package meta if already given
|
||||
promise = this._hasNew(pkgMeta);
|
||||
|
||||
return promise.fin(function () {
|
||||
return promise.fin(function() {
|
||||
that._working = false;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype.resolve = function () {
|
||||
Resolver.prototype.resolve = function() {
|
||||
var that = this;
|
||||
|
||||
// If already working, error out
|
||||
@@ -74,89 +74,104 @@ Resolver.prototype.resolve = function () {
|
||||
this._working = true;
|
||||
|
||||
// Create temporary dir
|
||||
return this._createTempDir()
|
||||
// Resolve self
|
||||
.then(this._resolve.bind(this))
|
||||
// Read json, generating the package meta
|
||||
.then(this._readJson.bind(this, null))
|
||||
// Apply and save package meta
|
||||
.then(function (meta) {
|
||||
return that._applyPkgMeta(meta)
|
||||
.then(that._savePkgMeta.bind(that, meta));
|
||||
})
|
||||
.then(function () {
|
||||
// Resolve with the folder
|
||||
return that._tempDir;
|
||||
}, function (err) {
|
||||
// If something went wrong, unset the temporary dir
|
||||
that._tempDir = null;
|
||||
throw err;
|
||||
})
|
||||
.fin(function () {
|
||||
that._working = false;
|
||||
});
|
||||
return (
|
||||
this._createTempDir()
|
||||
// Resolve self
|
||||
.then(this._resolve.bind(this))
|
||||
// Read json, generating the package meta
|
||||
.then(this._readJson.bind(this, null))
|
||||
// Apply and save package meta
|
||||
.then(function(meta) {
|
||||
return that
|
||||
._applyPkgMeta(meta)
|
||||
.then(that._savePkgMeta.bind(that, meta));
|
||||
})
|
||||
.then(
|
||||
function() {
|
||||
// Resolve with the folder
|
||||
return that._tempDir;
|
||||
},
|
||||
function(err) {
|
||||
// If something went wrong, unset the temporary dir
|
||||
that._tempDir = null;
|
||||
throw err;
|
||||
}
|
||||
)
|
||||
.fin(function() {
|
||||
that._working = false;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
Resolver.prototype.isCacheable = function () {
|
||||
Resolver.prototype.isCacheable = function() {
|
||||
// Bypass cache for local dependencies
|
||||
if (this._source &&
|
||||
if (
|
||||
this._source &&
|
||||
/^(?:file:[\/\\]{2}|[A-Z]:)?\.?\.?[\/\\]/.test(this._source)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't want to cache moving targets like branches
|
||||
if (this._pkgMeta &&
|
||||
if (
|
||||
this._pkgMeta &&
|
||||
this._pkgMeta._resolution &&
|
||||
this._pkgMeta._resolution.type === 'branch') {
|
||||
this._pkgMeta._resolution.type === 'branch'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
// -----------------
|
||||
|
||||
// Abstract functions that must be implemented by concrete resolvers
|
||||
Resolver.prototype._resolve = function () {
|
||||
Resolver.prototype._resolve = function() {
|
||||
throw new Error('_resolve not implemented');
|
||||
};
|
||||
|
||||
// Abstract functions that can be re-implemented by concrete resolvers
|
||||
// as necessary
|
||||
Resolver.prototype._hasNew = function (pkgMeta) {
|
||||
Resolver.prototype._hasNew = function(pkgMeta) {
|
||||
return Q.resolve(true);
|
||||
};
|
||||
|
||||
Resolver.isTargetable = function () {
|
||||
Resolver.isTargetable = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
Resolver.versions = function (source) {
|
||||
Resolver.versions = function(source) {
|
||||
return Q.resolve([]);
|
||||
};
|
||||
|
||||
Resolver.clearRuntimeCache = function () {};
|
||||
Resolver.clearRuntimeCache = function() {};
|
||||
|
||||
// -----------------
|
||||
|
||||
Resolver.prototype._createTempDir = function () {
|
||||
Resolver.prototype._createTempDir = function() {
|
||||
return Q.nfcall(mkdirp, this._config.tmp)
|
||||
.then(function () {
|
||||
return Q.nfcall(tmp.dir, {
|
||||
template: path.join(this._config.tmp, md5(this._name) + '-' + process.pid + '-XXXXXX'),
|
||||
mode: 0777 & ~process.umask(),
|
||||
unsafeCleanup: true
|
||||
});
|
||||
}.bind(this))
|
||||
.then(function (dir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return this._tempDir = Array.isArray(dir) ? dir[0] : dir;
|
||||
}.bind(this));
|
||||
.then(
|
||||
function() {
|
||||
return Q.nfcall(tmp.dir, {
|
||||
template: path.join(
|
||||
this._config.tmp,
|
||||
md5(this._name) + '-' + process.pid + '-XXXXXX'
|
||||
),
|
||||
mode: 0777 & ~process.umask(),
|
||||
unsafeCleanup: true
|
||||
});
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function(dir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return (this._tempDir = Array.isArray(dir) ? dir[0] : dir);
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
Resolver.prototype._cleanTempDir = function () {
|
||||
Resolver.prototype._cleanTempDir = function() {
|
||||
var tempDir = this._tempDir;
|
||||
|
||||
if (!tempDir) {
|
||||
@@ -165,31 +180,37 @@ Resolver.prototype._cleanTempDir = function () {
|
||||
|
||||
// Delete and create folder
|
||||
return Q.nfcall(rimraf, tempDir)
|
||||
.then(function () {
|
||||
return Q.nfcall(mkdirp, tempDir, 0777 & ~process.umask());
|
||||
})
|
||||
.then(function () {
|
||||
return tempDir;
|
||||
});
|
||||
.then(function() {
|
||||
return Q.nfcall(mkdirp, tempDir, 0777 & ~process.umask());
|
||||
})
|
||||
.then(function() {
|
||||
return tempDir;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype._readJson = function (dir) {
|
||||
Resolver.prototype._readJson = function(dir) {
|
||||
var that = this;
|
||||
|
||||
dir = dir || this._tempDir;
|
||||
return readJson(dir, {
|
||||
assume: { name: this._name }
|
||||
})
|
||||
.spread(function (json, deprecated) {
|
||||
assume: { name: this._name },
|
||||
logger: that._logger
|
||||
}).spread(function(json, deprecated) {
|
||||
if (deprecated) {
|
||||
that._logger.warn('deprecated', 'Package ' + that._name + ' is using the deprecated ' + deprecated);
|
||||
that._logger.warn(
|
||||
'deprecated',
|
||||
'Package ' +
|
||||
that._name +
|
||||
' is using the deprecated ' +
|
||||
deprecated
|
||||
);
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype._applyPkgMeta = function (meta) {
|
||||
Resolver.prototype._applyPkgMeta = function(meta) {
|
||||
// Check if name defined in the json is different
|
||||
// If so and if the name was "guessed", assume the json name
|
||||
if (meta.name !== this._name && this._guessedName) {
|
||||
@@ -203,13 +224,12 @@ Resolver.prototype._applyPkgMeta = function (meta) {
|
||||
}
|
||||
|
||||
// Otherwise remove them from the temp dir
|
||||
return removeIgnores(this._tempDir, meta)
|
||||
.then(function () {
|
||||
return removeIgnores(this._tempDir, meta).then(function() {
|
||||
return meta;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype._savePkgMeta = function (meta) {
|
||||
Resolver.prototype._savePkgMeta = function(meta) {
|
||||
var that = this;
|
||||
var contents;
|
||||
|
||||
@@ -217,21 +237,15 @@ Resolver.prototype._savePkgMeta = function (meta) {
|
||||
meta._source = this._source;
|
||||
meta._target = this._target;
|
||||
|
||||
['main', 'ignore'].forEach(function (attr) {
|
||||
if (meta[attr]) return;
|
||||
|
||||
that._logger.log(
|
||||
'warn', 'invalid-meta',
|
||||
(meta.name || 'component') + ' is missing "' + attr + '" entry in bower.json'
|
||||
);
|
||||
});
|
||||
|
||||
// Stringify contents
|
||||
contents = JSON.stringify(meta, null, 2);
|
||||
|
||||
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
|
||||
.then(function () {
|
||||
return that._pkgMeta = meta;
|
||||
return Q.nfcall(
|
||||
fs.writeFile,
|
||||
path.join(this._tempDir, '.bower.json'),
|
||||
contents
|
||||
).then(function() {
|
||||
return (that._pkgMeta = meta);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -31,27 +31,29 @@ mout.object.mixIn(SvnResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.getSource = function (source) {
|
||||
SvnResolver.getSource = function(source) {
|
||||
var uri = this._source || source;
|
||||
|
||||
return uri
|
||||
.replace(/^svn\+(https?|file):\/\//i, '$1://') // Change svn+http or svn+https or svn+file to http(s), file respectively
|
||||
.replace('svn://', 'http://') // Change svn to http
|
||||
.replace(/\/+$/, ''); // Remove trailing slashes
|
||||
.replace(/^svn\+(https?|file):\/\//i, '$1://') // Change svn+http or svn+https or svn+file to http(s), file respectively
|
||||
.replace('svn://', 'http://') // Change svn to http
|
||||
.replace(/\/+$/, ''); // Remove trailing slashes
|
||||
};
|
||||
|
||||
SvnResolver.prototype._hasNew = function (pkgMeta) {
|
||||
SvnResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldResolution = pkgMeta._resolution || {};
|
||||
|
||||
return this._findResolution()
|
||||
.then(function (resolution) {
|
||||
return this._findResolution().then(function(resolution) {
|
||||
// Check if resolution types are different
|
||||
if (oldResolution.type !== resolution.type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If resolved to a version, there is new content if the tags are not equal
|
||||
if (resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag)) {
|
||||
if (
|
||||
resolution.type === 'version' &&
|
||||
semver.neq(resolution.tag, oldResolution.tag)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,18 +62,17 @@ SvnResolver.prototype._hasNew = function (pkgMeta) {
|
||||
});
|
||||
};
|
||||
|
||||
SvnResolver.prototype._resolve = function () {
|
||||
SvnResolver.prototype._resolve = function() {
|
||||
var that = this;
|
||||
|
||||
return this._findResolution()
|
||||
.then(function () {
|
||||
return this._findResolution().then(function() {
|
||||
return that._export();
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._export = function () {
|
||||
SvnResolver.prototype._export = function() {
|
||||
var promise;
|
||||
var timer;
|
||||
var reporter;
|
||||
@@ -80,27 +81,56 @@ SvnResolver.prototype._export = function () {
|
||||
|
||||
this.source = SvnResolver.getSource(this._source);
|
||||
|
||||
this._logger.action('export', resolution.tag || resolution.branch || resolution.commit, {
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
});
|
||||
this._logger.action(
|
||||
'export',
|
||||
resolution.tag || resolution.branch || resolution.commit,
|
||||
{
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
}
|
||||
);
|
||||
|
||||
if (resolution.type === 'commit') {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/trunk',
|
||||
'-r' + resolution.commit,
|
||||
this._tempDir
|
||||
]);
|
||||
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/trunk', this._tempDir]);
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/trunk',
|
||||
this._tempDir
|
||||
]);
|
||||
} else if (resolution.type === 'branch') {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/branches/' + resolution.branch, this._tempDir]);
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/branches/' + resolution.branch,
|
||||
this._tempDir
|
||||
]);
|
||||
} else {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/tags/' + resolution.tag, this._tempDir]);
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/tags/' + resolution.tag,
|
||||
this._tempDir
|
||||
]);
|
||||
}
|
||||
|
||||
// Throttle the progress reporter to 1 time each sec
|
||||
reporter = mout.fn.throttle(function (data) {
|
||||
reporter = mout.fn.throttle(function(data) {
|
||||
var lines;
|
||||
|
||||
lines = data.split(/[\r\n]+/);
|
||||
lines.forEach(function (line) {
|
||||
lines.forEach(function(line) {
|
||||
if (/\d{1,3}\%/.test(line)) {
|
||||
// TODO: There are some strange chars that appear once in a while (\u001b[K)
|
||||
// Trim also those?
|
||||
@@ -110,25 +140,27 @@ SvnResolver.prototype._export = function () {
|
||||
}, 1000);
|
||||
|
||||
// Start reporting progress after a few seconds
|
||||
timer = setTimeout(function () {
|
||||
timer = setTimeout(function() {
|
||||
promise.progress(reporter);
|
||||
}, 8000);
|
||||
|
||||
return promise
|
||||
// Add additional proxy information to the error if necessary
|
||||
.fail(function (err) {
|
||||
throw err;
|
||||
})
|
||||
// Clear timer at the end
|
||||
.fin(function () {
|
||||
clearTimeout(timer);
|
||||
reporter.cancel();
|
||||
});
|
||||
return (
|
||||
promise
|
||||
// Add additional proxy information to the error if necessary
|
||||
.fail(function(err) {
|
||||
throw err;
|
||||
})
|
||||
// Clear timer at the end
|
||||
.fin(function() {
|
||||
clearTimeout(timer);
|
||||
reporter.cancel();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._findResolution = function (target) {
|
||||
SvnResolver.prototype._findResolution = function(target) {
|
||||
var err;
|
||||
var self = this.constructor;
|
||||
var that = this;
|
||||
@@ -139,7 +171,7 @@ SvnResolver.prototype._findResolution = function (target) {
|
||||
|
||||
// Target is a revision, so it's a stale target (not a moving target)
|
||||
// There's nothing to do in this case
|
||||
if ((/^r\d+/).test(target)) {
|
||||
if (/^r\d+/.test(target)) {
|
||||
target = target.split('r');
|
||||
|
||||
this._resolution = { type: 'commit', commit: target[1] };
|
||||
@@ -148,13 +180,12 @@ SvnResolver.prototype._findResolution = function (target) {
|
||||
|
||||
// Target is a range/version
|
||||
if (semver.validRange(target)) {
|
||||
return self.versions(this._source, true)
|
||||
.then(function (versions) {
|
||||
var versionsArr,
|
||||
version,
|
||||
index;
|
||||
return self.versions(this._source, true).then(function(versions) {
|
||||
var versionsArr, version, index;
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
versionsArr = versions.map(function(obj) {
|
||||
return obj.version;
|
||||
});
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on trunk
|
||||
@@ -162,80 +193,124 @@ SvnResolver.prototype._findResolution = function (target) {
|
||||
return that._findResolution('trunk');
|
||||
}
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
versionsArr = versions.map(function(obj) {
|
||||
return obj.version;
|
||||
});
|
||||
// Find a satisfying version, enabling strict match so that pre-releases
|
||||
// have lower priority over normal ones when target is *
|
||||
index = semver.maxSatisfyingIndex(versionsArr, target, true);
|
||||
if (index !== -1) {
|
||||
version = versions[index];
|
||||
return that._resolution = { type: 'version', tag: version.tag, commit: version.commit };
|
||||
return (that._resolution = {
|
||||
type: 'version',
|
||||
tag: version.tag,
|
||||
commit: version.commit
|
||||
});
|
||||
}
|
||||
|
||||
// Check if there's an exact branch/tag with this name as last resort
|
||||
return Q.all([
|
||||
self.branches(that._source),
|
||||
self.tags(that._source)
|
||||
])
|
||||
.spread(function (branches, tags) {
|
||||
]).spread(function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[target]
|
||||
});
|
||||
}
|
||||
|
||||
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
|
||||
details: !versions.length ?
|
||||
'No versions found in ' + that._source :
|
||||
'Available versions in ' + that._source + ': ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
});
|
||||
throw createError(
|
||||
'No tag found that was able to satisfy ' + target,
|
||||
'ENORESTARGET',
|
||||
{
|
||||
details: !versions.length
|
||||
? 'No versions found in ' + that._source
|
||||
: 'Available versions in ' +
|
||||
that._source +
|
||||
': ' +
|
||||
versions
|
||||
.map(function(version) {
|
||||
return version.version;
|
||||
})
|
||||
.join(', ')
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise, target is either a tag or a branch
|
||||
return Q.all([
|
||||
self.branches(that._source),
|
||||
self.tags(that._source)
|
||||
])
|
||||
.spread(function (branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
|
||||
return Q.all([self.branches(that._source), self.tags(that._source)]).spread(
|
||||
function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[target]
|
||||
});
|
||||
}
|
||||
|
||||
branches = Object.keys(branches);
|
||||
tags = Object.keys(tags);
|
||||
|
||||
err = createError(
|
||||
'target ' + target + ' does not exist',
|
||||
'ENORESTARGET'
|
||||
);
|
||||
err.details = !tags.length
|
||||
? 'No tags found in ' + that._source
|
||||
: 'Available tags: ' + tags.join(', ');
|
||||
err.details += '\n';
|
||||
err.details += !branches.length
|
||||
? 'No branches found in ' + that._source
|
||||
: 'Available branches: ' + branches.join(', ');
|
||||
|
||||
throw err;
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
|
||||
}
|
||||
|
||||
branches = Object.keys(branches);
|
||||
tags = Object.keys(tags);
|
||||
|
||||
err = createError('target ' + target + ' does not exist', 'ENORESTARGET');
|
||||
err.details = !tags.length ?
|
||||
'No tags found in ' + that._source :
|
||||
'Available tags: ' + tags.join(', ');
|
||||
err.details += '\n';
|
||||
err.details += !branches.length ?
|
||||
'No branches found in ' + that._source :
|
||||
'Available branches: ' + branches.join(', ');
|
||||
|
||||
throw err;
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
SvnResolver.prototype._savePkgMeta = function (meta) {
|
||||
SvnResolver.prototype._savePkgMeta = function(meta) {
|
||||
var version;
|
||||
|
||||
if (this._resolution.type === 'version') {
|
||||
version = semver.clean(this._resolution.tag);
|
||||
|
||||
// Warn if the package meta version is different than the resolved one
|
||||
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
|
||||
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
|
||||
resolution: this._resolution,
|
||||
pkgMeta: meta
|
||||
});
|
||||
if (
|
||||
typeof meta.version === 'string' &&
|
||||
semver.neq(meta.version, version)
|
||||
) {
|
||||
this._logger.warn(
|
||||
'mismatch',
|
||||
'Version declared in the json (' +
|
||||
meta.version +
|
||||
') is different than the resolved one (' +
|
||||
version +
|
||||
')',
|
||||
{
|
||||
resolution: this._resolution,
|
||||
pkgMeta: meta
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure package meta version is the same as the resolution
|
||||
@@ -249,9 +324,7 @@ SvnResolver.prototype._savePkgMeta = function (meta) {
|
||||
// Save version/tag/commit in the release
|
||||
// Note that we can't store branches because _release is supposed to be
|
||||
// an unique id of this ref.
|
||||
meta._release = version ||
|
||||
this._resolution.tag ||
|
||||
this._resolution.commit;
|
||||
meta._release = version || this._resolution.tag || this._resolution.commit;
|
||||
|
||||
// Save resolution to be used in hasNew later
|
||||
meta._resolution = this._resolution;
|
||||
@@ -261,52 +334,58 @@ SvnResolver.prototype._savePkgMeta = function (meta) {
|
||||
|
||||
// ------------------------------
|
||||
|
||||
SvnResolver.versions = function (source, extra) {
|
||||
SvnResolver.versions = function(source, extra) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.versions.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value)
|
||||
.then(function () {
|
||||
var versions = this._cache.versions.get(source);
|
||||
return Q.resolve(value).then(
|
||||
function() {
|
||||
var versions = this._cache.versions.get(source);
|
||||
|
||||
// If no extra information was requested,
|
||||
// resolve simply with the versions
|
||||
if (!extra) {
|
||||
versions = versions.map(function (version) {
|
||||
return version.version;
|
||||
});
|
||||
}
|
||||
// If no extra information was requested,
|
||||
// resolve simply with the versions
|
||||
if (!extra) {
|
||||
versions = versions.map(function(version) {
|
||||
return version.version;
|
||||
});
|
||||
}
|
||||
|
||||
return versions;
|
||||
}.bind(this));
|
||||
return versions;
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
value = this.tags(source)
|
||||
.then(function (tags) {
|
||||
var tag;
|
||||
var version;
|
||||
var versions = [];
|
||||
value = this.tags(source).then(
|
||||
function(tags) {
|
||||
var tag;
|
||||
var version;
|
||||
var versions = [];
|
||||
|
||||
// For each tag
|
||||
for (tag in tags) {
|
||||
version = semver.clean(tag);
|
||||
if (version) {
|
||||
versions.push({ version: version, tag: tag, commit: tags[tag] });
|
||||
// For each tag
|
||||
for (tag in tags) {
|
||||
version = semver.clean(tag);
|
||||
if (version) {
|
||||
versions.push({
|
||||
version: version,
|
||||
tag: tag,
|
||||
commit: tags[tag]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by DESC order
|
||||
versions.sort(function (a, b) {
|
||||
return semver.rcompare(a.version, b.version);
|
||||
});
|
||||
// Sort them by DESC order
|
||||
versions.sort(function(a, b) {
|
||||
return semver.rcompare(a.version, b.version);
|
||||
});
|
||||
|
||||
this._cache.versions.set(source, versions);
|
||||
this._cache.versions.set(source, versions);
|
||||
|
||||
// Call the function again to keep it DRY
|
||||
return this.versions(source, extra);
|
||||
}.bind(this));
|
||||
// Call the function again to keep it DRY
|
||||
return this.versions(source, extra);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
@@ -315,7 +394,7 @@ SvnResolver.versions = function (source, extra) {
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.tags = function (source) {
|
||||
SvnResolver.tags = function(source) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.tags.get(source);
|
||||
@@ -324,14 +403,19 @@ SvnResolver.tags = function (source) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', ['list', source + '/tags', '--verbose', '--non-interactive'])
|
||||
.spread(function (stout) {
|
||||
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
|
||||
value = cmd('svn', [
|
||||
'list',
|
||||
source + '/tags',
|
||||
'--verbose',
|
||||
'--non-interactive'
|
||||
]).spread(
|
||||
function(stout) {
|
||||
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
|
||||
|
||||
this._cache.tags.set(source, tags);
|
||||
return tags;
|
||||
|
||||
}.bind(this));
|
||||
this._cache.tags.set(source, tags);
|
||||
return tags;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
@@ -340,7 +424,7 @@ SvnResolver.tags = function (source) {
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.branches = function (source) {
|
||||
SvnResolver.branches = function(source) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.branches.get(source);
|
||||
@@ -349,17 +433,24 @@ SvnResolver.branches = function (source) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', ['list', source + '/branches', '--verbose', '--non-interactive'])
|
||||
.spread(function (stout) {
|
||||
var branches = SvnResolver.parseSubversionListOutput(stout.toString());
|
||||
value = cmd('svn', [
|
||||
'list',
|
||||
source + '/branches',
|
||||
'--verbose',
|
||||
'--non-interactive'
|
||||
]).spread(
|
||||
function(stout) {
|
||||
var branches = SvnResolver.parseSubversionListOutput(
|
||||
stout.toString()
|
||||
);
|
||||
|
||||
// trunk is a branch!
|
||||
branches.trunk = '*';
|
||||
// trunk is a branch!
|
||||
branches.trunk = '*';
|
||||
|
||||
this._cache.branches.set(source, branches);
|
||||
return branches;
|
||||
|
||||
}.bind(this));
|
||||
this._cache.branches.set(source, branches);
|
||||
return branches;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
@@ -368,15 +459,12 @@ SvnResolver.branches = function (source) {
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.parseSubversionListOutput = function (stout) {
|
||||
|
||||
SvnResolver.parseSubversionListOutput = function(stout) {
|
||||
var entries = {};
|
||||
var lines = stout
|
||||
.trim()
|
||||
.split(/[\r\n]+/);
|
||||
var lines = stout.trim().split(/[\r\n]+/);
|
||||
|
||||
// For each line in the refs, match only the branches
|
||||
lines.forEach(function (line) {
|
||||
lines.forEach(function(line) {
|
||||
var match = line.match(/\s+([0-9]+)\s.+\s([\w.$-]+)\//i);
|
||||
|
||||
if (match && match[2] !== '.') {
|
||||
@@ -387,9 +475,9 @@ SvnResolver.parseSubversionListOutput = function (stout) {
|
||||
return entries;
|
||||
};
|
||||
|
||||
SvnResolver.clearRuntimeCache = function () {
|
||||
SvnResolver.clearRuntimeCache = function() {
|
||||
// Reset cache for branches, tags, etc
|
||||
mout.object.forOwn(SvnResolver._cache, function (lru) {
|
||||
mout.object.forOwn(SvnResolver._cache, function(lru) {
|
||||
lru.reset();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ function UrlResolver(decEndpoint, config, logger) {
|
||||
|
||||
// If target was specified, error out
|
||||
if (this._target !== '*') {
|
||||
throw createError('URL sources can\'t resolve targets', 'ENORESTARGET');
|
||||
throw createError("URL sources can't resolve targets", 'ENORESTARGET');
|
||||
}
|
||||
|
||||
// If the name was guessed
|
||||
@@ -24,7 +24,10 @@ function UrlResolver(decEndpoint, config, logger) {
|
||||
// Remove the ?xxx part
|
||||
this._name = this._name.replace(/\?.*$/, '');
|
||||
// Remove extension
|
||||
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
|
||||
this._name = this._name.substr(
|
||||
0,
|
||||
this._name.length - path.extname(this._name).length
|
||||
);
|
||||
}
|
||||
|
||||
this._remote = url.parse(this._source);
|
||||
@@ -35,11 +38,11 @@ mout.object.mixIn(UrlResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
UrlResolver.isTargetable = function () {
|
||||
UrlResolver.isTargetable = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
UrlResolver.prototype._hasNew = function (pkgMeta) {
|
||||
UrlResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldCacheHeaders = pkgMeta._cacheHeaders || {};
|
||||
var reqHeaders = {};
|
||||
|
||||
@@ -54,61 +57,71 @@ UrlResolver.prototype._hasNew = function (pkgMeta) {
|
||||
}
|
||||
|
||||
// Make an HEAD request to the source
|
||||
return Q.nfcall(request.head, this._source, {
|
||||
ca: this._config.ca.default,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
})
|
||||
// Compare new headers with the old ones
|
||||
.spread(function (response) {
|
||||
var cacheHeaders;
|
||||
return (
|
||||
Q.nfcall(request.head, this._source, {
|
||||
ca: this._config.ca.default,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
})
|
||||
// Compare new headers with the old ones
|
||||
.spread(
|
||||
function(response) {
|
||||
var cacheHeaders;
|
||||
|
||||
// If the server responded with 303 then the resource
|
||||
// still has the same ETag
|
||||
if (response.statusCode === 304) {
|
||||
return false;
|
||||
}
|
||||
// If the server responded with 303 then the resource
|
||||
// still has the same ETag
|
||||
if (response.statusCode === 304) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If status code is not in the 2xx range,
|
||||
// then just resolve to true
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
return true;
|
||||
}
|
||||
// If status code is not in the 2xx range,
|
||||
// then just resolve to true
|
||||
if (
|
||||
response.statusCode < 200 ||
|
||||
response.statusCode >= 300
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback to comparing cache headers
|
||||
cacheHeaders = this._collectCacheHeaders(response);
|
||||
return !mout.object.equals(oldCacheHeaders, cacheHeaders);
|
||||
}.bind(this), function () {
|
||||
// Assume new contents if the request failed
|
||||
// Note that we do not retry the request using the "request-replay" module
|
||||
// because it would take too long
|
||||
return true;
|
||||
});
|
||||
// Fallback to comparing cache headers
|
||||
cacheHeaders = this._collectCacheHeaders(response);
|
||||
return !mout.object.equals(oldCacheHeaders, cacheHeaders);
|
||||
}.bind(this),
|
||||
function() {
|
||||
// Assume new contents if the request failed
|
||||
// Note that we do not retry the request using the "request-replay" module
|
||||
// because it would take too long
|
||||
return true;
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: There's room for improvement by using streams if the URL
|
||||
// is an archive file, by piping read stream to the zip extractor
|
||||
// This will likely increase the complexity of code but might worth it
|
||||
|
||||
UrlResolver.prototype._resolve = function () {
|
||||
UrlResolver.prototype._resolve = function() {
|
||||
// Download
|
||||
return this._download()
|
||||
// Parse headers
|
||||
.spread(this._parseHeaders.bind(this))
|
||||
// Extract file
|
||||
.spread(this._extract.bind(this))
|
||||
// Rename file to index
|
||||
.then(this._rename.bind(this));
|
||||
return (
|
||||
this._download()
|
||||
// Parse headers
|
||||
.spread(this._parseHeaders.bind(this))
|
||||
// Extract file
|
||||
.spread(this._extract.bind(this))
|
||||
// Rename file to index
|
||||
.then(this._rename.bind(this))
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
UrlResolver.prototype._parseSourceURL = function (_url) {
|
||||
UrlResolver.prototype._parseSourceURL = function(_url) {
|
||||
return url.parse(path.basename(_url)).pathname;
|
||||
};
|
||||
|
||||
UrlResolver.prototype._download = function () {
|
||||
UrlResolver.prototype._download = function() {
|
||||
var fileName = this._parseSourceURL(this._source);
|
||||
|
||||
if (!fileName) {
|
||||
@@ -136,32 +149,43 @@ UrlResolver.prototype._download = function () {
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
})
|
||||
.progress(function (state) {
|
||||
var msg;
|
||||
.progress(function(state) {
|
||||
var msg;
|
||||
|
||||
// Retry?
|
||||
if (state.retry) {
|
||||
msg = 'Download of ' + that._source + ' failed' + (state.error.code ? ' with ' + state.error.code : '') + ', ';
|
||||
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.debug('error', state.error.message, { error: state.error });
|
||||
return that._logger.warn('retry', msg);
|
||||
}
|
||||
// Retry?
|
||||
if (state.retry) {
|
||||
msg =
|
||||
'Download of ' +
|
||||
that._source +
|
||||
' failed' +
|
||||
(state.error.code ? ' with ' + state.error.code : '') +
|
||||
', ';
|
||||
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.debug('error', state.error.message, {
|
||||
error: state.error
|
||||
});
|
||||
return that._logger.warn('retry', msg);
|
||||
}
|
||||
|
||||
// Progress
|
||||
msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
|
||||
if (state.total) {
|
||||
msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
|
||||
msg += state.percent + '%';
|
||||
}
|
||||
that._logger.info('progress', msg);
|
||||
})
|
||||
.then(function (response) {
|
||||
that._response = response;
|
||||
return [file, response];
|
||||
});
|
||||
// Progress
|
||||
msg =
|
||||
'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
|
||||
if (state.total) {
|
||||
msg +=
|
||||
' of ' +
|
||||
(state.total / 1024 / 1024).toFixed(1) +
|
||||
'MB downloaded, ';
|
||||
msg += state.percent + '%';
|
||||
}
|
||||
that._logger.info('progress', msg);
|
||||
})
|
||||
.then(function(response) {
|
||||
that._response = response;
|
||||
return [file, response];
|
||||
});
|
||||
};
|
||||
|
||||
UrlResolver.prototype._parseHeaders = function (file, response) {
|
||||
UrlResolver.prototype._parseHeaders = function(file, response) {
|
||||
var disposition;
|
||||
var newFile;
|
||||
var match;
|
||||
@@ -196,20 +220,19 @@ UrlResolver.prototype._parseHeaders = function (file, response) {
|
||||
|
||||
newFile = path.join(this._tempDir, newFile);
|
||||
|
||||
return Q.nfcall(fs.rename, file, newFile)
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.rename, file, newFile).then(function() {
|
||||
return [newFile, response];
|
||||
});
|
||||
};
|
||||
|
||||
UrlResolver.prototype._extract = function (file, response) {
|
||||
UrlResolver.prototype._extract = function(file, response) {
|
||||
var mimeType = response.headers['content-type'];
|
||||
|
||||
if (mimeType) {
|
||||
// Clean everything after ; and trim the end result
|
||||
mimeType = mimeType.split(';')[0].trim();
|
||||
// Some servers add quotes around the content-type, so we trim that also
|
||||
mimeType = mout.string.trim(mimeType, ['"', '\'']);
|
||||
mimeType = mout.string.trim(mimeType, ['"', "'"]);
|
||||
}
|
||||
|
||||
if (!extract.canExtract(file, mimeType)) {
|
||||
@@ -226,36 +249,42 @@ UrlResolver.prototype._extract = function (file, response) {
|
||||
});
|
||||
};
|
||||
|
||||
UrlResolver.prototype._rename = function () {
|
||||
return Q.nfcall(fs.readdir, this._tempDir)
|
||||
.then(function (files) {
|
||||
var file;
|
||||
var oldPath;
|
||||
var newPath;
|
||||
UrlResolver.prototype._rename = function() {
|
||||
return Q.nfcall(fs.readdir, this._tempDir).then(
|
||||
function(files) {
|
||||
var file;
|
||||
var oldPath;
|
||||
var newPath;
|
||||
|
||||
// Remove any OS specific files from the files array
|
||||
// before checking its length
|
||||
files = files.filter(junk.isnt);
|
||||
// Remove any OS specific files from the files array
|
||||
// before checking its length
|
||||
files = files.filter(junk.isnt);
|
||||
|
||||
// Only rename if there's only one file and it's not the json
|
||||
if (files.length === 1 && !/^(component|bower)\.json$/.test(files[0])) {
|
||||
file = files[0];
|
||||
this._singleFile = 'index' + path.extname(file);
|
||||
oldPath = path.join(this._tempDir, file);
|
||||
newPath = path.join(this._tempDir, this._singleFile);
|
||||
// Only rename if there's only one file and it's not the json
|
||||
if (
|
||||
files.length === 1 &&
|
||||
!/^(component|bower)\.json$/.test(files[0])
|
||||
) {
|
||||
file = files[0];
|
||||
this._singleFile = 'index' + path.extname(file);
|
||||
oldPath = path.join(this._tempDir, file);
|
||||
newPath = path.join(this._tempDir, this._singleFile);
|
||||
|
||||
return Q.nfcall(fs.rename, oldPath, newPath);
|
||||
}
|
||||
}.bind(this));
|
||||
return Q.nfcall(fs.rename, oldPath, newPath);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
UrlResolver.prototype._savePkgMeta = function (meta) {
|
||||
UrlResolver.prototype._savePkgMeta = function(meta) {
|
||||
// Store collected headers in the package meta
|
||||
meta._cacheHeaders = this._collectCacheHeaders(this._response);
|
||||
|
||||
// Store ETAG under _release
|
||||
if (meta._cacheHeaders.ETag) {
|
||||
meta._release = 'e-tag:' + mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
|
||||
meta._release =
|
||||
'e-tag:' +
|
||||
mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
|
||||
}
|
||||
|
||||
// Store main if is a single file
|
||||
@@ -266,11 +295,11 @@ UrlResolver.prototype._savePkgMeta = function (meta) {
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
UrlResolver.prototype._collectCacheHeaders = function (res) {
|
||||
UrlResolver.prototype._collectCacheHeaders = function(res) {
|
||||
var headers = {};
|
||||
|
||||
// Collect cache headers
|
||||
this.constructor._cacheHeaders.forEach(function (name) {
|
||||
this.constructor._cacheHeaders.forEach(function(name) {
|
||||
var value = res.headers[name.toLowerCase()];
|
||||
|
||||
if (value != null) {
|
||||
|
||||
@@ -12,7 +12,12 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
bower = bower || {};
|
||||
|
||||
if (typeof resolverFactory !== 'function') {
|
||||
throw createError('Resolver has "' + typeof resolverFactory + '" type instead of "function" type.', 'ERESOLERAPI');
|
||||
throw createError(
|
||||
'Resolver has "' +
|
||||
typeof resolverFactory +
|
||||
'" type instead of "function" type.',
|
||||
'ERESOLERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
var resolver = resolverFactory(bower);
|
||||
@@ -20,7 +25,9 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
function maxSatisfyingVersion(versions, target) {
|
||||
var versionsArr, index;
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
versionsArr = versions.map(function(obj) {
|
||||
return obj.version;
|
||||
});
|
||||
|
||||
// Find a satisfying version, enabling strict match so that pre-releases
|
||||
// have lower priority over normal ones when target is *
|
||||
@@ -36,7 +43,7 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
}
|
||||
|
||||
// @private
|
||||
PluginResolver.prototype.getEndpoint = function () {
|
||||
PluginResolver.prototype.getEndpoint = function() {
|
||||
return object.merge(this._decEndpoint, {
|
||||
name: this.getName(),
|
||||
source: this.getSource(),
|
||||
@@ -44,15 +51,15 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getSource = function () {
|
||||
PluginResolver.prototype.getSource = function() {
|
||||
return this._decEndpoint.source;
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getTarget = function () {
|
||||
PluginResolver.prototype.getTarget = function() {
|
||||
return this._decEndpoint.target || '*';
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getName = function () {
|
||||
PluginResolver.prototype.getName = function() {
|
||||
if (!this._decEndpoint.name && typeof resolver.getName === 'function') {
|
||||
return resolver.getName.call(resolver, this.getSource());
|
||||
} else if (!this._decEndpoint.name) {
|
||||
@@ -62,7 +69,7 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
}
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getPkgMeta = function () {
|
||||
PluginResolver.prototype.getPkgMeta = function() {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
@@ -70,32 +77,32 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
|
||||
// Plugin Resolver is always considered potentially cacheable
|
||||
// The "resolve" method decides whether to use cached or fetch new version.
|
||||
PluginResolver.prototype.isCacheable = function () {
|
||||
PluginResolver.prototype.isCacheable = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Not only it's always potentially cacheable, but also always potenially new.
|
||||
// The "resolve" handles logic of re-downloading target if needed.
|
||||
PluginResolver.prototype.hasNew = function (pkgMeta) {
|
||||
PluginResolver.prototype.hasNew = function(pkgMeta) {
|
||||
if (this.hasNewPromise) {
|
||||
return this.hasNewPromise;
|
||||
}
|
||||
|
||||
this._pkgMeta = pkgMeta;
|
||||
|
||||
return this.hasNewPromise = this.resolve().then(function (result) {
|
||||
return (this.hasNewPromise = this.resolve().then(function(result) {
|
||||
return result !== undefined;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
PluginResolver.prototype.resolve = function () {
|
||||
PluginResolver.prototype.resolve = function() {
|
||||
if (this.resolvePromise) {
|
||||
return this.resolvePromise;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
|
||||
return this.resolvePromise = Q.fcall(function () {
|
||||
return (this.resolvePromise = Q.fcall(function() {
|
||||
var target = that.getTarget();
|
||||
|
||||
// It means that we can accept ranges as targets
|
||||
@@ -103,15 +110,19 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
that._release = target;
|
||||
|
||||
if (semver.validRange(target)) {
|
||||
return Q.fcall(resolver.releases.bind(resolver), that.getSource())
|
||||
.then(function (result) {
|
||||
return Q.fcall(
|
||||
resolver.releases.bind(resolver),
|
||||
that.getSource()
|
||||
).then(function(result) {
|
||||
if (!result) {
|
||||
throw createError('Resolver did not provide releases of package.');
|
||||
throw createError(
|
||||
'Resolver did not provide releases of package.'
|
||||
);
|
||||
}
|
||||
|
||||
var releases = this._releases = result;
|
||||
var releases = (that._releases = result);
|
||||
|
||||
var versions = releases.filter(function (target) {
|
||||
var versions = releases.filter(function(target) {
|
||||
return semver.clean(target.version);
|
||||
});
|
||||
|
||||
@@ -119,90 +130,122 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
|
||||
if (maxRelease) {
|
||||
that._version = maxRelease.version;
|
||||
that._release = that._decEndpoint.target = maxRelease.target;
|
||||
that._release = that._decEndpoint.target =
|
||||
maxRelease.target;
|
||||
} else {
|
||||
throw createError('No version found that was able to satisfy ' + target, 'ENORESTARGET', {
|
||||
details: !versions.length ?
|
||||
'No versions found in ' + that.getSource() :
|
||||
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
});
|
||||
throw createError(
|
||||
'No version found that was able to satisfy ' +
|
||||
target,
|
||||
'ENORESTARGET',
|
||||
{
|
||||
details: !versions.length
|
||||
? 'No versions found in ' +
|
||||
that.getSource()
|
||||
: 'Available versions: ' +
|
||||
versions
|
||||
.map(function(version) {
|
||||
return version.version;
|
||||
})
|
||||
.join(', ')
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (semver.validRange(target) && target !== '*') {
|
||||
return Q.reject(createError('Resolver does not accept version ranges (' + target + ')'));
|
||||
return Q.reject(
|
||||
createError(
|
||||
'Resolver does not accept version ranges (' +
|
||||
target +
|
||||
')'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
|
||||
// We pass old _resolution (if hasNew has been called before contents).
|
||||
// So resolver can decide wheter use cached version of contents new one.
|
||||
if (typeof resolver.fetch !== 'function') {
|
||||
throw createError('Resolver does not implement the "fetch" method.');
|
||||
}
|
||||
|
||||
var cached = {};
|
||||
|
||||
if (that._releases) {
|
||||
cached.releases = that._releases;
|
||||
}
|
||||
|
||||
if (that._pkgMeta) {
|
||||
cached.endpoint = {
|
||||
name: that._pkgMeta.name,
|
||||
source: that._pkgMeta._source,
|
||||
target: that._pkgMeta._target
|
||||
};
|
||||
|
||||
cached.release = that._pkgMeta._release;
|
||||
|
||||
cached.version = that._pkgMeta.version;
|
||||
|
||||
cached.resolution = that._pkgMeta._resolution || {};
|
||||
}
|
||||
|
||||
return Q.fcall(resolver.fetch.bind(resolver), that.getEndpoint(), cached);
|
||||
})
|
||||
.then(function (result) {
|
||||
// Empty result means to re-use existing resolution
|
||||
if (!result) {
|
||||
return;
|
||||
} else {
|
||||
if (!result.tempPath) {
|
||||
throw createError('Resolver did not provide path to extracted contents of package.');
|
||||
.then(function() {
|
||||
// We pass old _resolution (if hasNew has been called before contents).
|
||||
// So resolver can decide wheter use cached version of contents new one.
|
||||
if (typeof resolver.fetch !== 'function') {
|
||||
throw createError(
|
||||
'Resolver does not implement the "fetch" method.'
|
||||
);
|
||||
}
|
||||
|
||||
that._tempDir = result.tempPath;
|
||||
var cached = {};
|
||||
|
||||
return that._readJson(that._tempDir).then(function (meta) {
|
||||
return that._applyPkgMeta(meta, result)
|
||||
.then(that._savePkgMeta.bind(that, meta, result))
|
||||
.then(function () {
|
||||
return that._tempDir;
|
||||
if (that._releases) {
|
||||
cached.releases = that._releases;
|
||||
}
|
||||
|
||||
if (that._pkgMeta) {
|
||||
cached.endpoint = {
|
||||
name: that._pkgMeta.name,
|
||||
source: that._pkgMeta._source,
|
||||
target: that._pkgMeta._target
|
||||
};
|
||||
|
||||
cached.release = that._pkgMeta._release;
|
||||
|
||||
cached.version = that._pkgMeta.version;
|
||||
|
||||
cached.resolution = that._pkgMeta._resolution || {};
|
||||
}
|
||||
|
||||
return Q.fcall(
|
||||
resolver.fetch.bind(resolver),
|
||||
that.getEndpoint(),
|
||||
cached
|
||||
);
|
||||
})
|
||||
.then(function(result) {
|
||||
// Empty result means to re-use existing resolution
|
||||
if (!result) {
|
||||
return;
|
||||
} else {
|
||||
if (!result.tempPath) {
|
||||
throw createError(
|
||||
'Resolver did not provide path to extracted contents of package.'
|
||||
);
|
||||
}
|
||||
|
||||
that._tempDir = result.tempPath;
|
||||
|
||||
return that._readJson(that._tempDir).then(function(meta) {
|
||||
return that
|
||||
._applyPkgMeta(meta, result)
|
||||
.then(that._savePkgMeta.bind(that, meta, result))
|
||||
.then(function() {
|
||||
return that._tempDir;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
PluginResolver.prototype._readJson = function (dir) {
|
||||
PluginResolver.prototype._readJson = function(dir) {
|
||||
var that = this;
|
||||
|
||||
return readJson(dir, {
|
||||
assume: { name: that.getName() }
|
||||
})
|
||||
.spread(function (json, deprecated) {
|
||||
assume: { name: that.getName() },
|
||||
logger: bower.logger
|
||||
}).spread(function(json, deprecated) {
|
||||
if (deprecated) {
|
||||
bower.logger.warn('deprecated', 'Package ' + that.getName() + ' is using the deprecated ' + deprecated);
|
||||
bower.logger.warn(
|
||||
'deprecated',
|
||||
'Package ' +
|
||||
that.getName() +
|
||||
' is using the deprecated ' +
|
||||
deprecated
|
||||
);
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype._applyPkgMeta = function (meta, result) {
|
||||
PluginResolver.prototype._applyPkgMeta = function(meta, result) {
|
||||
// Check if name defined in the json is different
|
||||
// If so and if the name was "guessed", assume the json name
|
||||
if (meta.name !== this._name) {
|
||||
@@ -211,17 +254,21 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
|
||||
// Handle ignore property, deleting all files from the temporary directory
|
||||
// If no ignores were specified, simply resolve
|
||||
if (result.removeIgnores === false || !meta.ignore || !meta.ignore.length) {
|
||||
if (
|
||||
result.removeIgnores === false ||
|
||||
!meta.ignore ||
|
||||
!meta.ignore.length
|
||||
) {
|
||||
return Q.resolve(meta);
|
||||
}
|
||||
|
||||
// Otherwise remove them from the temp dir
|
||||
return removeIgnores(this._tempDir, meta).then(function () {
|
||||
return removeIgnores(this._tempDir, meta).then(function() {
|
||||
return meta;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype._savePkgMeta = function (meta, result) {
|
||||
PluginResolver.prototype._savePkgMeta = function(meta, result) {
|
||||
var that = this;
|
||||
|
||||
meta._source = that.getSource();
|
||||
@@ -244,30 +291,37 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
// Stringify contents
|
||||
var contents = JSON.stringify(meta, null, 2);
|
||||
|
||||
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
|
||||
.then(function () {
|
||||
return that._pkgMeta = meta;
|
||||
return Q.nfcall(
|
||||
fs.writeFile,
|
||||
path.join(this._tempDir, '.bower.json'),
|
||||
contents
|
||||
).then(function() {
|
||||
return (that._pkgMeta = meta);
|
||||
});
|
||||
};
|
||||
|
||||
// It is used only by "bower info". It returns all semver versions.
|
||||
PluginResolver.versions = function (source) {
|
||||
return Q.fcall(resolver.releases.bind(resolver), source).then(function (result) {
|
||||
PluginResolver.versions = function(source) {
|
||||
return Q.fcall(resolver.releases.bind(resolver), source).then(function(
|
||||
result
|
||||
) {
|
||||
if (!result) {
|
||||
throw createError('Resolver did not provide releases of package.');
|
||||
throw createError(
|
||||
'Resolver did not provide releases of package.'
|
||||
);
|
||||
}
|
||||
|
||||
var releases = this._releases = result;
|
||||
var releases = (this._releases = result);
|
||||
|
||||
var versions = releases.map(function (version) {
|
||||
var versions = releases.map(function(version) {
|
||||
return semver.clean(version.version);
|
||||
});
|
||||
|
||||
versions = versions.filter(function (version) {
|
||||
versions = versions.filter(function(version) {
|
||||
return version;
|
||||
});
|
||||
|
||||
versions.sort(function (a, b) {
|
||||
versions.sort(function(a, b) {
|
||||
return semver.rcompare(a, b);
|
||||
});
|
||||
|
||||
@@ -275,39 +329,50 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.isTargetable = function () {
|
||||
PluginResolver.isTargetable = function() {
|
||||
// If resolver doesn't define versions function, it's not targetable..
|
||||
return typeof resolver.releases === 'function';
|
||||
};
|
||||
|
||||
PluginResolver.clearRuntimeCache = function () {
|
||||
PluginResolver.clearRuntimeCache = function() {
|
||||
resolver = resolverFactory(bower);
|
||||
};
|
||||
|
||||
PluginResolver.match = function (source) {
|
||||
PluginResolver.match = function(source) {
|
||||
if (typeof resolver.match !== 'function') {
|
||||
throw createError('Resolver is missing "match" method.', 'ERESOLVERAPI');
|
||||
throw createError(
|
||||
'Resolver is missing "match" method.',
|
||||
'ERESOLVERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
var match = resolver.match.bind(resolver);
|
||||
|
||||
return Q.fcall(match, source).then(function (result) {
|
||||
return Q.fcall(match, source).then(function(result) {
|
||||
if (typeof result !== 'boolean') {
|
||||
throw createError('Resolver\'s "match" method should return a boolean', 'ERESOLVERAPI');
|
||||
throw createError(
|
||||
'Resolver\'s "match" method should return a boolean',
|
||||
'ERESOLVERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.locate = function (source) {
|
||||
PluginResolver.locate = function(source) {
|
||||
if (typeof resolver.locate !== 'function') {
|
||||
return source;
|
||||
}
|
||||
|
||||
return Q.fcall(resolver.locate.bind(resolver), source).then(function (result) {
|
||||
return Q.fcall(resolver.locate.bind(resolver), source).then(function(
|
||||
result
|
||||
) {
|
||||
if (typeof result !== 'string') {
|
||||
throw createError('Resolver\'s "locate" method should return a string', 'ERESOLVERAPI');
|
||||
throw createError(
|
||||
'Resolver\'s "locate" method should return a string',
|
||||
'ERESOLVERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -317,6 +382,4 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
return PluginResolver;
|
||||
}
|
||||
|
||||
|
||||
module.exports = pluginResolverFactory;
|
||||
|
||||
|
||||
@@ -3,28 +3,36 @@ var cmd = require('../util/cmd');
|
||||
var Q = require('q');
|
||||
var shellquote = require('shell-quote');
|
||||
|
||||
var orderByDependencies = function (packages, installed, json) {
|
||||
var orderByDependencies = function(packages, installed, json) {
|
||||
var ordered = [];
|
||||
installed = mout.object.keys(installed);
|
||||
|
||||
var depsSatisfied = function (packageName) {
|
||||
return mout.array.difference(mout.object.keys(packages[packageName].dependencies), installed, ordered).length === 0;
|
||||
var depsSatisfied = function(packageName) {
|
||||
return (
|
||||
mout.array.difference(
|
||||
mout.object.keys(packages[packageName].dependencies),
|
||||
installed,
|
||||
ordered
|
||||
).length === 0
|
||||
);
|
||||
};
|
||||
|
||||
var depsFromBowerJson = json && json.dependencies ? mout.object.keys(json.dependencies) : [];
|
||||
var depsFromBowerJson =
|
||||
json && json.dependencies ? mout.object.keys(json.dependencies) : [];
|
||||
var packageNames = mout.object.keys(packages);
|
||||
|
||||
//get the list of the packages that are specified in bower.json in that order
|
||||
//its nice to maintain that order for users
|
||||
var desiredOrder = mout.array.intersection(depsFromBowerJson, packageNames);
|
||||
//then add to the end any remaining packages that werent in bower.json
|
||||
desiredOrder = desiredOrder.concat(mout.array.difference(packageNames, desiredOrder));
|
||||
desiredOrder = desiredOrder.concat(
|
||||
mout.array.difference(packageNames, desiredOrder)
|
||||
);
|
||||
|
||||
//the desired order isn't necessarily a correct dependency specific order
|
||||
//so we ensure that below
|
||||
var resolvedOne = true;
|
||||
while (resolvedOne) {
|
||||
|
||||
resolvedOne = false;
|
||||
|
||||
for (var i = 0; i < desiredOrder.length; i++) {
|
||||
@@ -43,17 +51,16 @@ var orderByDependencies = function (packages, installed, json) {
|
||||
//so lets just jam those names on the end
|
||||
ordered = ordered.concat(desiredOrder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ordered;
|
||||
};
|
||||
|
||||
var run = function (cmdString, action, logger, config) {
|
||||
var run = function(cmdString, action, logger, config) {
|
||||
logger.action(action, cmdString);
|
||||
|
||||
//pass env + BOWER_PID so callees can identify a preinstall+postinstall from the same bower instance
|
||||
var env = mout.object.mixIn({ 'BOWER_PID': process.pid }, process.env);
|
||||
var env = mout.object.mixIn({ BOWER_PID: process.pid }, process.env);
|
||||
var args = shellquote.parse(cmdString, env);
|
||||
var cmdName = args[0];
|
||||
mout.array.remove(args, cmdName); //no rest() in mout
|
||||
@@ -65,8 +72,8 @@ var run = function (cmdString, action, logger, config) {
|
||||
|
||||
var promise = cmd(cmdName, args, options);
|
||||
|
||||
promise.progress(function (progress) {
|
||||
progress.split('\n').forEach(function (line) {
|
||||
promise.progress(function(progress) {
|
||||
progress.split('\n').forEach(function(line) {
|
||||
if (line) {
|
||||
logger.action(action, line);
|
||||
}
|
||||
@@ -76,19 +83,38 @@ var run = function (cmdString, action, logger, config) {
|
||||
return promise;
|
||||
};
|
||||
|
||||
var hook = function (action, ordered, config, logger, packages, installed, json) {
|
||||
if (mout.object.keys(packages).length === 0 || !config.scripts || !config.scripts[action]) {
|
||||
var hook = function(
|
||||
action,
|
||||
ordered,
|
||||
config,
|
||||
logger,
|
||||
packages,
|
||||
installed,
|
||||
json
|
||||
) {
|
||||
if (
|
||||
mout.object.keys(packages).length === 0 ||
|
||||
!config.scripts ||
|
||||
!config.scripts[action]
|
||||
) {
|
||||
return Q();
|
||||
}
|
||||
|
||||
var orderedPackages = ordered ? orderByDependencies(packages, installed, json) : mout.object.keys(packages);
|
||||
var orderedPackages = ordered
|
||||
? orderByDependencies(packages, installed, json)
|
||||
: mout.object.keys(packages);
|
||||
var placeholder = new RegExp('%', 'g');
|
||||
var cmdString = mout.string.replace(config.scripts[action], placeholder, orderedPackages.join(' '));
|
||||
var cmdString = mout.string.replace(
|
||||
config.scripts[action],
|
||||
placeholder,
|
||||
orderedPackages.join(' ')
|
||||
);
|
||||
return run(cmdString, action, logger, config);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
preuninstall: mout.function.partial(hook, 'preuninstall', false),
|
||||
postuninstall: mout.function.partial(hook, 'postuninstall', false),
|
||||
preinstall: mout.function.partial(hook, 'preinstall', true),
|
||||
postinstall: mout.function.partial(hook, 'postinstall', true),
|
||||
//only exposed for test
|
||||
|
||||
@@ -7,7 +7,7 @@ function JsonRenderer() {
|
||||
this._nrLogs = 0;
|
||||
}
|
||||
|
||||
JsonRenderer.prototype.end = function (data) {
|
||||
JsonRenderer.prototype.end = function(data) {
|
||||
if (this._nrLogs) {
|
||||
process.stderr.write(']\n');
|
||||
}
|
||||
@@ -17,7 +17,7 @@ JsonRenderer.prototype.end = function (data) {
|
||||
}
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.error = function (err) {
|
||||
JsonRenderer.prototype.error = function(err) {
|
||||
var message = err.message;
|
||||
var stack;
|
||||
|
||||
@@ -32,13 +32,13 @@ JsonRenderer.prototype.error = function (err) {
|
||||
|
||||
// Stack
|
||||
stack = err.fstream_stack || err.stack || 'N/A';
|
||||
err.stacktrace = (Array.isArray(stack) ? stack.join('\n') : stack);
|
||||
err.stacktrace = Array.isArray(stack) ? stack.join('\n') : stack;
|
||||
|
||||
this.log(err);
|
||||
this.end();
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.log = function (log) {
|
||||
JsonRenderer.prototype.log = function(log) {
|
||||
if (!this._nrLogs) {
|
||||
process.stderr.write('[');
|
||||
} else {
|
||||
@@ -49,12 +49,12 @@ JsonRenderer.prototype.log = function (log) {
|
||||
this._nrLogs++;
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.prompt = function (prompts) {
|
||||
JsonRenderer.prototype.prompt = function(prompts) {
|
||||
var promise = Q.resolve();
|
||||
var answers = {};
|
||||
var that = this;
|
||||
|
||||
prompts.forEach(function (prompt) {
|
||||
prompts.forEach(function(prompt) {
|
||||
var opts;
|
||||
var funcName;
|
||||
|
||||
@@ -63,65 +63,68 @@ JsonRenderer.prototype.prompt = function (prompts) {
|
||||
|
||||
// Prompt
|
||||
opts = {
|
||||
silent: true, // To not mess with JSON output
|
||||
trim: false, // To allow " " to not assume the default value
|
||||
default: prompt.default == null ? '' : prompt.default, // If default is null, make it '' so that it does not retry
|
||||
validator: !prompt.validate ? null : function (value) {
|
||||
var ret = prompt.validate(value);
|
||||
silent: true, // To not mess with JSON output
|
||||
trim: false, // To allow " " to not assume the default value
|
||||
default: prompt.default == null ? '' : prompt.default, // If default is null, make it '' so that it does not retry
|
||||
validator: !prompt.validate
|
||||
? null
|
||||
: function(value) {
|
||||
var ret = prompt.validate(value);
|
||||
|
||||
if (typeof ret === 'string') {
|
||||
throw ret;
|
||||
}
|
||||
if (typeof ret === 'string') {
|
||||
throw ret;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// For now only "input", "confirm" and "password" are supported
|
||||
switch (prompt.type) {
|
||||
case 'input':
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
case 'confirm':
|
||||
case 'password':
|
||||
funcName = prompt.type;
|
||||
break;
|
||||
case 'checkbox':
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
default:
|
||||
promise = promise.then(function () {
|
||||
throw createError('Unknown prompt type', 'ENOTSUP');
|
||||
});
|
||||
return;
|
||||
case 'input':
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
case 'confirm':
|
||||
case 'password':
|
||||
funcName = prompt.type;
|
||||
break;
|
||||
case 'checkbox':
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
default:
|
||||
promise = promise.then(function() {
|
||||
throw createError('Unknown prompt type', 'ENOTSUP');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
promise = promise.then(function () {
|
||||
promise = promise.then(function() {
|
||||
// Log
|
||||
prompt.level = 'prompt';
|
||||
that.log(prompt);
|
||||
|
||||
return Q.nfcall(promptly[funcName], '', opts)
|
||||
.then(function (answer) {
|
||||
return Q.nfcall(promptly[funcName], '', opts).then(function(
|
||||
answer
|
||||
) {
|
||||
answers[prompt.name] = answer;
|
||||
});
|
||||
});
|
||||
|
||||
if (prompt.type === 'checkbox') {
|
||||
promise = promise.then(function () {
|
||||
promise = promise.then(function() {
|
||||
answers[prompt.name] = answers[prompt.name].split(',');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return promise.then(function () {
|
||||
return promise.then(function() {
|
||||
return answers;
|
||||
});
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
JsonRenderer.prototype._stringify = function (log) {
|
||||
JsonRenderer.prototype._stringify = function(log) {
|
||||
// To json
|
||||
var str = JSON.stringify(log, null, ' ');
|
||||
// Remove colors in case some log has colors..
|
||||
|
||||
@@ -11,9 +11,9 @@ var template = require('../util/template');
|
||||
|
||||
function StandardRenderer(command, config) {
|
||||
this._sizes = {
|
||||
id: 13, // Id max chars
|
||||
id: 13, // Id max chars
|
||||
label: 20, // Label max chars
|
||||
sumup: 5 // Amount to sum when the label exceeds
|
||||
sumup: 5 // Amount to sum when the label exceeds
|
||||
};
|
||||
this._colors = {
|
||||
warn: chalk.yellow,
|
||||
@@ -32,7 +32,7 @@ function StandardRenderer(command, config) {
|
||||
this._compact = process.stdout.columns < 120;
|
||||
}
|
||||
|
||||
var exitOnPipeError = function (err) {
|
||||
var exitOnPipeError = function(err) {
|
||||
if (err.code === 'EPIPE') {
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -43,7 +43,7 @@ function StandardRenderer(command, config) {
|
||||
process.stderr.on('error', exitOnPipeError);
|
||||
}
|
||||
|
||||
StandardRenderer.prototype.end = function (data) {
|
||||
StandardRenderer.prototype.end = function(data) {
|
||||
var method = '_' + mout.string.camelCase(this._command);
|
||||
|
||||
if (this[method]) {
|
||||
@@ -51,7 +51,7 @@ StandardRenderer.prototype.end = function (data) {
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.error = function (err) {
|
||||
StandardRenderer.prototype.error = function(err) {
|
||||
var str;
|
||||
var stack;
|
||||
|
||||
@@ -60,12 +60,19 @@ StandardRenderer.prototype.error = function (err) {
|
||||
err.id = err.code || 'error';
|
||||
err.level = 'error';
|
||||
|
||||
str = this._prefix(err) + ' ' + err.message.replace(/\r?\n/g, ' ').trim() + '\n';
|
||||
str =
|
||||
this._prefix(err) +
|
||||
' ' +
|
||||
err.message.replace(/\r?\n/g, ' ').trim() +
|
||||
'\n';
|
||||
this._write(process.stderr, 'bower ' + str);
|
||||
|
||||
// Check if additional details were provided
|
||||
if (err.details) {
|
||||
str = chalk.yellow('\nAdditional error details:\n') + err.details.trim() + '\n';
|
||||
str =
|
||||
chalk.yellow('\nAdditional error details:\n') +
|
||||
err.details.trim() +
|
||||
'\n';
|
||||
this._write(process.stderr, str);
|
||||
}
|
||||
|
||||
@@ -83,12 +90,18 @@ StandardRenderer.prototype.error = function (err) {
|
||||
// Print bower version, node version and system info.
|
||||
this._write(process.stderr, chalk.yellow('\nSystem info:\n'));
|
||||
this._write(process.stderr, 'Bower version: ' + version + '\n');
|
||||
this._write(process.stderr, 'Node version: ' + process.versions.node + '\n');
|
||||
this._write(process.stderr, 'OS: ' + os.type() + ' ' + os.release() + ' ' + os.arch() + '\n');
|
||||
this._write(
|
||||
process.stderr,
|
||||
'Node version: ' + process.versions.node + '\n'
|
||||
);
|
||||
this._write(
|
||||
process.stderr,
|
||||
'OS: ' + os.type() + ' ' + os.release() + ' ' + os.arch() + '\n'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.log = function (log) {
|
||||
StandardRenderer.prototype.log = function(log) {
|
||||
var method = '_' + mout.string.camelCase(log.id) + 'Log';
|
||||
|
||||
this._guessOrigin(log);
|
||||
@@ -101,12 +114,12 @@ StandardRenderer.prototype.log = function (log) {
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.prompt = function (prompts) {
|
||||
StandardRenderer.prototype.prompt = function(prompts) {
|
||||
var deferred;
|
||||
|
||||
// Strip colors from the prompt if color is disabled
|
||||
if (!this._config.color) {
|
||||
prompts.forEach(function (prompt) {
|
||||
prompts.forEach(function(prompt) {
|
||||
prompt.message = chalk.stripColor(prompt.message);
|
||||
});
|
||||
}
|
||||
@@ -121,7 +134,7 @@ StandardRenderer.prototype.prompt = function (prompts) {
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._help = function (data) {
|
||||
StandardRenderer.prototype._help = function(data) {
|
||||
var str;
|
||||
var that = this;
|
||||
var specific;
|
||||
@@ -136,60 +149,68 @@ StandardRenderer.prototype._help = function (data) {
|
||||
if (template.exists(specific)) {
|
||||
str = template.render(specific, data);
|
||||
} else {
|
||||
str = template.render('std/help-generic.std', data);
|
||||
str = template.render('std/help-generic.std', data);
|
||||
}
|
||||
|
||||
that._write(process.stdout, str);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._install = function (packages) {
|
||||
StandardRenderer.prototype._install = function(packages) {
|
||||
var str = '';
|
||||
|
||||
mout.object.forOwn(packages, function (pkg) {
|
||||
var cliTree;
|
||||
mout.object.forOwn(
|
||||
packages,
|
||||
function(pkg) {
|
||||
var cliTree;
|
||||
|
||||
// List only 1 level deep dependencies
|
||||
mout.object.forOwn(pkg.dependencies, function (dependency) {
|
||||
dependency.dependencies = {};
|
||||
});
|
||||
// Make canonical dir relative
|
||||
pkg.canonicalDir = path.relative(this._config.cwd, pkg.canonicalDir);
|
||||
// Signal as root
|
||||
pkg.root = true;
|
||||
// List only 1 level deep dependencies
|
||||
mout.object.forOwn(pkg.dependencies, function(dependency) {
|
||||
dependency.dependencies = {};
|
||||
});
|
||||
// Make canonical dir relative
|
||||
pkg.canonicalDir = path.relative(
|
||||
this._config.cwd,
|
||||
pkg.canonicalDir
|
||||
);
|
||||
// Signal as root
|
||||
pkg.root = true;
|
||||
|
||||
cliTree = this._tree2archy(pkg);
|
||||
str += '\n' + archy(cliTree);
|
||||
}, this);
|
||||
cliTree = this._tree2archy(pkg);
|
||||
str += '\n' + archy(cliTree);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
if (str) {
|
||||
this._write(process.stdout, str);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._update = function (packages) {
|
||||
StandardRenderer.prototype._update = function(packages) {
|
||||
this._install(packages);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._list = function (tree) {
|
||||
StandardRenderer.prototype._list = function(tree) {
|
||||
var cliTree;
|
||||
|
||||
if (tree.pkgMeta) {
|
||||
tree.root = true;
|
||||
cliTree = archy(this._tree2archy(tree));
|
||||
} else {
|
||||
cliTree = stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
|
||||
cliTree =
|
||||
stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
|
||||
}
|
||||
|
||||
this._write(process.stdout, cliTree);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._search = function (results) {
|
||||
StandardRenderer.prototype._search = function(results) {
|
||||
var str = template.render('std/search-results.std', results);
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._info = function (data) {
|
||||
StandardRenderer.prototype._info = function(data) {
|
||||
var str = '';
|
||||
var pkgMeta = data;
|
||||
var includeVersions = false;
|
||||
@@ -212,7 +233,7 @@ StandardRenderer.prototype._info = function (data) {
|
||||
data.numPreReleases = 0;
|
||||
// If output isn't verbose, hide prereleases
|
||||
if (!this._config.verbose) {
|
||||
data.versions = mout.array.filter(data.versions, function (version) {
|
||||
data.versions = mout.array.filter(data.versions, function(version) {
|
||||
version = semverUtils.parse(version);
|
||||
if (!version.release && !version.build) {
|
||||
return true;
|
||||
@@ -227,13 +248,13 @@ StandardRenderer.prototype._info = function (data) {
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._lookup = function (data) {
|
||||
StandardRenderer.prototype._lookup = function(data) {
|
||||
var str = template.render('std/lookup.std', data);
|
||||
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._link = function (data) {
|
||||
StandardRenderer.prototype._link = function(data) {
|
||||
this._sizes.id = 4;
|
||||
|
||||
this.log({
|
||||
@@ -248,7 +269,7 @@ StandardRenderer.prototype._link = function (data) {
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._register = function (data) {
|
||||
StandardRenderer.prototype._register = function(data) {
|
||||
var str;
|
||||
|
||||
// If no data passed, it means the user aborted
|
||||
@@ -260,17 +281,20 @@ StandardRenderer.prototype._register = function (data) {
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._cacheList = function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
StandardRenderer.prototype._cacheList = function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
var pkgMeta = entry.pkgMeta;
|
||||
var version = pkgMeta.version || pkgMeta._target;
|
||||
this._write(process.stdout, pkgMeta.name + '=' + pkgMeta._source + '#' + version + '\n');
|
||||
this._write(
|
||||
process.stdout,
|
||||
pkgMeta.name + '=' + pkgMeta._source + '#' + version + '\n'
|
||||
);
|
||||
}, this);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._genericLog = function (log) {
|
||||
StandardRenderer.prototype._genericLog = function(log) {
|
||||
var stream;
|
||||
var str;
|
||||
|
||||
@@ -284,7 +308,7 @@ StandardRenderer.prototype._genericLog = function (log) {
|
||||
this._write(stream, 'bower ' + str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._checkoutLog = function (log) {
|
||||
StandardRenderer.prototype._checkoutLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin.split('#')[0] + '#' + log.message;
|
||||
}
|
||||
@@ -292,7 +316,7 @@ StandardRenderer.prototype._checkoutLog = function (log) {
|
||||
this._genericLog(log);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._progressLog = function (log) {
|
||||
StandardRenderer.prototype._progressLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin + ' ' + log.message;
|
||||
}
|
||||
@@ -300,7 +324,7 @@ StandardRenderer.prototype._progressLog = function (log) {
|
||||
this._genericLog(log);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._extractLog = function (log) {
|
||||
StandardRenderer.prototype._extractLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin + ' ' + log.message;
|
||||
}
|
||||
@@ -308,19 +332,23 @@ StandardRenderer.prototype._extractLog = function (log) {
|
||||
this._genericLog(log);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._incompatibleLog = function (log) {
|
||||
StandardRenderer.prototype._incompatibleLog = function(log) {
|
||||
var str;
|
||||
var templatePath;
|
||||
|
||||
// Generate dependants string for each pick
|
||||
log.data.picks.forEach(function (pick) {
|
||||
pick.dependants = pick.dependants.map(function (dependant) {
|
||||
var release = dependant.pkgMeta._release;
|
||||
return dependant.endpoint.name + (release ? '#' + release : '');
|
||||
}).join(', ');
|
||||
log.data.picks.forEach(function(pick) {
|
||||
pick.dependants = pick.dependants
|
||||
.map(function(dependant) {
|
||||
var release = dependant.pkgMeta._release;
|
||||
return dependant.endpoint.name + (release ? '#' + release : '');
|
||||
})
|
||||
.join(', ');
|
||||
});
|
||||
|
||||
templatePath = log.data.suitable ? 'std/conflict-resolved.std' : 'std/conflict.std';
|
||||
templatePath = log.data.suitable
|
||||
? 'std/conflict-resolved.std'
|
||||
: 'std/conflict.std';
|
||||
str = template.render(templatePath, log.data);
|
||||
|
||||
this._write(process.stdout, '\n');
|
||||
@@ -328,15 +356,18 @@ StandardRenderer.prototype._incompatibleLog = function (log) {
|
||||
this._write(process.stdout, '\n');
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._solvedLog = function (log) {
|
||||
StandardRenderer.prototype._solvedLog = function(log) {
|
||||
this._incompatibleLog(log);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._jsonLog = function (log) {
|
||||
this._write(process.stdout, '\n' + this._highlightJson(log.data.json) + '\n\n');
|
||||
StandardRenderer.prototype._jsonLog = function(log) {
|
||||
this._write(
|
||||
process.stdout,
|
||||
'\n' + this._highlightJson(log.data.json) + '\n\n'
|
||||
);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._cachedEntryLog = function (log) {
|
||||
StandardRenderer.prototype._cachedEntryLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin;
|
||||
}
|
||||
@@ -346,7 +377,7 @@ StandardRenderer.prototype._cachedEntryLog = function (log) {
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._guessOrigin = function (log) {
|
||||
StandardRenderer.prototype._guessOrigin = function(log) {
|
||||
var data = log.data;
|
||||
|
||||
if (!data) {
|
||||
@@ -354,7 +385,8 @@ StandardRenderer.prototype._guessOrigin = function (log) {
|
||||
}
|
||||
|
||||
if (data.endpoint) {
|
||||
log.origin = data.endpoint.name || (data.registry && data.endpoint.source);
|
||||
log.origin =
|
||||
data.endpoint.name || (data.registry && data.endpoint.source);
|
||||
|
||||
// Resort to using the resolver name for unnamed endpoints
|
||||
if (!log.origin && data.resolver) {
|
||||
@@ -373,7 +405,7 @@ StandardRenderer.prototype._guessOrigin = function (log) {
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._prefix = function (log) {
|
||||
StandardRenderer.prototype._prefix = function(log) {
|
||||
var label;
|
||||
var length;
|
||||
var nrSpaces;
|
||||
@@ -405,7 +437,7 @@ StandardRenderer.prototype._prefix = function (log) {
|
||||
return chalk.green(label) + mout.string.repeat(' ', nrSpaces) + idColor(id);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._write = function (stream, str) {
|
||||
StandardRenderer.prototype._write = function(stream, str) {
|
||||
if (!this._config.color) {
|
||||
str = chalk.stripColor(str);
|
||||
}
|
||||
@@ -413,18 +445,18 @@ StandardRenderer.prototype._write = function (stream, str) {
|
||||
stream.write(str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._highlightJson = function (json) {
|
||||
StandardRenderer.prototype._highlightJson = function(json) {
|
||||
var cardinal = require('cardinal');
|
||||
|
||||
return cardinal.highlight(stringifyObject(json, { indent: ' ' }), {
|
||||
theme: {
|
||||
String: {
|
||||
_default: function (str) {
|
||||
_default: function(str) {
|
||||
return chalk.cyan(str);
|
||||
}
|
||||
},
|
||||
Identifier: {
|
||||
_default: function (str) {
|
||||
_default: function(str) {
|
||||
return chalk.green(str);
|
||||
}
|
||||
}
|
||||
@@ -433,9 +465,11 @@ StandardRenderer.prototype._highlightJson = function (json) {
|
||||
});
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._tree2archy = function (node) {
|
||||
StandardRenderer.prototype._tree2archy = function(node) {
|
||||
var dependencies = mout.object.values(node.dependencies);
|
||||
var version = !node.missing ? node.pkgMeta._release || node.pkgMeta.version : null;
|
||||
var version = !node.missing
|
||||
? node.pkgMeta._release || node.pkgMeta.version
|
||||
: null;
|
||||
var label = node.endpoint.name + (version ? '#' + version : '');
|
||||
var update;
|
||||
|
||||
@@ -458,7 +492,8 @@ StandardRenderer.prototype._tree2archy = function (node) {
|
||||
}
|
||||
|
||||
if (node.incompatible) {
|
||||
label += chalk.yellow(' incompatible') + ' with ' + node.endpoint.target;
|
||||
label +=
|
||||
chalk.yellow(' incompatible') + ' with ' + node.endpoint.target;
|
||||
} else if (node.extraneous) {
|
||||
label += chalk.green(' extraneous');
|
||||
}
|
||||
@@ -472,7 +507,7 @@ StandardRenderer.prototype._tree2archy = function (node) {
|
||||
}
|
||||
|
||||
if (node.update.latest !== node.update.target) {
|
||||
update += (update ? ', ' : '');
|
||||
update += update ? ', ' : '';
|
||||
update += 'latest is ' + node.update.latest;
|
||||
}
|
||||
|
||||
@@ -500,7 +535,7 @@ StandardRenderer._wideCommands = [
|
||||
'register'
|
||||
];
|
||||
StandardRenderer._idMappings = {
|
||||
'mutual': 'conflict',
|
||||
mutual: 'conflict',
|
||||
'cached-entry': 'cached'
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
var chalk = require('chalk');
|
||||
|
||||
var templateColors = [
|
||||
'yellow',
|
||||
'green',
|
||||
'cyan',
|
||||
'red',
|
||||
'white',
|
||||
'magenta'
|
||||
];
|
||||
var templateColors = ['yellow', 'green', 'cyan', 'red', 'white', 'magenta'];
|
||||
|
||||
function colors(Handlebars) {
|
||||
templateColors.forEach(function (color) {
|
||||
Handlebars.registerHelper(color, function (context) {
|
||||
templateColors.forEach(function(color) {
|
||||
Handlebars.registerHelper(color, function(context) {
|
||||
return chalk[color](context.fn(this));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
var mout = require('mout');
|
||||
var leadLinesRegExp = /^\r?\n/;
|
||||
var multipleLinesRegExp = /\r?\n(\r?\n)+/mg;
|
||||
var multipleLinesRegExp = /\r?\n(\r?\n)+/gm;
|
||||
|
||||
function condense(Handlebars) {
|
||||
Handlebars.registerHelper('condense', function (context) {
|
||||
Handlebars.registerHelper('condense', function(context) {
|
||||
var str = context.fn(this);
|
||||
|
||||
// Remove multiple lines
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var mout = require('mout');
|
||||
|
||||
function indent(Handlebars) {
|
||||
Handlebars.registerHelper('indent', function (context) {
|
||||
Handlebars.registerHelper('indent', function(context) {
|
||||
var hash = context.hash;
|
||||
var indentStr = mout.string.repeat(' ', parseInt(hash.level, 10));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var mout = require('mout');
|
||||
|
||||
function rpad(Handlebars) {
|
||||
Handlebars.registerHelper('rpad', function (context) {
|
||||
Handlebars.registerHelper('rpad', function(context) {
|
||||
var hash = context.hash;
|
||||
var minLength = parseInt(hash.minLength, 10);
|
||||
var chr = hash.char;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
function sum(Handlebars) {
|
||||
Handlebars.registerHelper('sum', function (val1, val2) {
|
||||
Handlebars.registerHelper('sum', function(val1, val2) {
|
||||
return val1 + val2;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"command": "lookup",
|
||||
"description": "Looks up a package URL by name.",
|
||||
"description": "Look up a single package URL by name.",
|
||||
"usage": [
|
||||
"lookup <name> [<options>]"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"command": "search",
|
||||
"description": "Finds all packages or a specific package.",
|
||||
"description": "Search for packages by name.",
|
||||
"usage": [
|
||||
"search <name> [<options>]"
|
||||
],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"command": "version",
|
||||
"description": "Run this in a package directory to bump the version and write the new data back to the bower.json file.\n\nThe newversion argument should be a valid semver string, or a valid second argument to semver.inc (one of \"build\", \"patch\", \"minor\", or \"major\"). In the second case, the existing version will be incremented\nby 1 in the specified field.\n\nIf run in a git repo, it will also create a version commit and tag, and fail if the repo is not clean.\n\nIf supplied with --message (shorthand: -m) config option, bower will use it as a commit message when creating a version commit. If the message config contains %s then that will be replaced with the resulting\nversion number. For example:\n\n bower version patch -m \"Upgrade to %s for reasons\"",
|
||||
"description": "Creates an empty version commit and tag, and fail if the repo is not clean.\n\nThe <version> argument should be a valid semver string, or one of following:\nbuild, patch, minor, major.\n\nIf supplied with --message (shorthand: -m) config option, bower will use it\nas a commit message when creating a version commit. If the message config\ncontains %s then that will be replaced with the resulting version number.\n\nFor example:\n\n bower version patch -m \"Upgrade to %s for reasons\"",
|
||||
"usage": [
|
||||
"version [<newversion> | major | minor | patch]"
|
||||
"version [<version> | major | minor | patch]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"link": "Symlink a package folder",
|
||||
"list": "List local packages - and possible updates",
|
||||
"login": "Authenticate with GitHub and store credentials",
|
||||
"lookup": "Look up a package URL by name",
|
||||
"lookup": "Look up a single package URL by name",
|
||||
"prune": "Removes local extraneous packages",
|
||||
"register": "Register a package",
|
||||
"search": "Search for a package by name",
|
||||
"search": "Search for packages by name",
|
||||
"update": "Update a local package",
|
||||
"uninstall": "Remove a local package",
|
||||
"unregister": "Remove a package from the registry",
|
||||
@@ -69,6 +69,10 @@
|
||||
{
|
||||
"flag": "--no-color",
|
||||
"description": "Disable colors"
|
||||
},
|
||||
{
|
||||
"flag": "--config.interactive=false",
|
||||
"description": "Disable prompts"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ Commands:
|
||||
|
||||
{{#condense}}
|
||||
{{#each commands}}
|
||||
{{#rpad length="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{#rpad minLength="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
{{#rpad minLength="23"}}{{/rpad}}
|
||||
|
||||
@@ -5,7 +5,7 @@ function expandNames(obj, prefix, stack) {
|
||||
prefix = prefix || '';
|
||||
stack = stack || [];
|
||||
|
||||
mout.object.forOwn(obj, function (value, name) {
|
||||
mout.object.forOwn(obj, function(value, name) {
|
||||
name = prefix + name;
|
||||
|
||||
stack.push(name);
|
||||
@@ -18,7 +18,7 @@ function expandNames(obj, prefix, stack) {
|
||||
return stack;
|
||||
}
|
||||
|
||||
module.exports = function (commands) {
|
||||
module.exports = function(commands) {
|
||||
var abbreviations = abbrev(expandNames(commands));
|
||||
|
||||
abbreviations.i = 'install';
|
||||
|
||||
@@ -15,10 +15,10 @@ function readOptions(options, argv) {
|
||||
options = options || {};
|
||||
}
|
||||
|
||||
types = mout.object.map(options, function (option) {
|
||||
types = mout.object.map(options, function(option) {
|
||||
return option.type;
|
||||
});
|
||||
mout.object.forOwn(options, function (option, name) {
|
||||
mout.object.forOwn(options, function(option, name) {
|
||||
shorthands[option.shorthand] = '--' + name;
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ function readOptions(options, argv) {
|
||||
|
||||
// Filter only the specified options because nopt parses every --
|
||||
// Also make them camel case
|
||||
mout.object.forOwn(noptOptions, function (value, key) {
|
||||
mout.object.forOwn(noptOptions, function(value, key) {
|
||||
if (options[key]) {
|
||||
parsedOptions[mout.string.camelCase(key)] = value;
|
||||
}
|
||||
|
||||
@@ -37,17 +37,17 @@ function getWindowsCommand(command) {
|
||||
try {
|
||||
fullCommand = which.sync(command);
|
||||
} catch (err) {
|
||||
return winWhichCache[command] = command;
|
||||
return (winWhichCache[command] = command);
|
||||
}
|
||||
|
||||
extension = path.extname(fullCommand).toLowerCase();
|
||||
|
||||
// Does it need to be converted?
|
||||
if (winBatchExtensions.indexOf(extension) === -1) {
|
||||
return winWhichCache[command] = command;
|
||||
return (winWhichCache[command] = command);
|
||||
}
|
||||
|
||||
return winWhichCache[command] = fullCommand;
|
||||
return (winWhichCache[command] = fullCommand);
|
||||
}
|
||||
|
||||
// Executes a shell command, buffering the stdout and stderr
|
||||
@@ -67,25 +67,25 @@ function executeCmd(command, args, options) {
|
||||
|
||||
// Buffer output, reporting progress
|
||||
process = cp.spawn(command, args, options);
|
||||
process.stdout.on('data', function (data) {
|
||||
process.stdout.on('data', function(data) {
|
||||
data = data.toString();
|
||||
deferred.notify(data);
|
||||
stdout += data;
|
||||
});
|
||||
process.stderr.on('data', function (data) {
|
||||
process.stderr.on('data', function(data) {
|
||||
data = data.toString();
|
||||
deferred.notify(data);
|
||||
stderr += data;
|
||||
});
|
||||
|
||||
// If there is an error spawning the command, reject the promise
|
||||
process.on('error', function (error) {
|
||||
process.on('error', function(error) {
|
||||
return deferred.reject(error);
|
||||
});
|
||||
|
||||
// Listen to the close event instead of exit
|
||||
// They are similar but close ensures that streams are flushed
|
||||
process.on('close', function (code) {
|
||||
process.on('close', function(code) {
|
||||
var fullCommand;
|
||||
var error;
|
||||
|
||||
@@ -99,10 +99,19 @@ function executeCmd(command, args, options) {
|
||||
fullCommand += args.length ? ' ' + args.join(' ') : '';
|
||||
|
||||
// Build the error instance
|
||||
error = createError('Failed to execute "' + fullCommand + '", exit code of #' + code + '\n' + stderr, 'ECMDERR', {
|
||||
details: stderr,
|
||||
exitCode: code
|
||||
});
|
||||
error = createError(
|
||||
'Failed to execute "' +
|
||||
fullCommand +
|
||||
'", exit code of #' +
|
||||
code +
|
||||
'\n' +
|
||||
stderr,
|
||||
'ECMDERR',
|
||||
{
|
||||
details: stderr,
|
||||
exitCode: code
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.reject(error);
|
||||
}
|
||||
|
||||
@@ -26,18 +26,17 @@ function copy(reader, writer) {
|
||||
deferred = Q.defer();
|
||||
|
||||
reader
|
||||
.on('error', deferred.reject)
|
||||
// Pipe to writer
|
||||
.pipe(fstream.Writer(writer))
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve);
|
||||
.on('error', deferred.reject)
|
||||
// Pipe to writer
|
||||
.pipe(fstream.Writer(writer))
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function copyMode(src, dst) {
|
||||
return Q.nfcall(fs.stat, src)
|
||||
.then(function (stat) {
|
||||
return Q.nfcall(fs.stat, src).then(function(stat) {
|
||||
return Q.nfcall(fs.chmod, dst, stat.mode);
|
||||
});
|
||||
}
|
||||
@@ -68,14 +67,17 @@ function copyFile(src, dst, opts) {
|
||||
|
||||
opts = parseOptions(opts);
|
||||
|
||||
promise = copy({
|
||||
path: src,
|
||||
type: 'File'
|
||||
}, {
|
||||
path: dst,
|
||||
mode: opts.mode,
|
||||
type: 'File'
|
||||
});
|
||||
promise = copy(
|
||||
{
|
||||
path: src,
|
||||
type: 'File'
|
||||
},
|
||||
{
|
||||
path: dst,
|
||||
mode: opts.mode,
|
||||
type: 'File'
|
||||
}
|
||||
);
|
||||
|
||||
if (opts.copyMode) {
|
||||
promise = promise.then(copyMode.bind(copyMode, src, dst));
|
||||
@@ -93,15 +95,18 @@ function copyDir(src, dst, opts) {
|
||||
|
||||
opts = parseOptions(opts);
|
||||
|
||||
promise = copy({
|
||||
path: src,
|
||||
type: 'Directory',
|
||||
ignore: opts.ignore
|
||||
}, {
|
||||
path: dst,
|
||||
mode: opts.mode,
|
||||
type: 'Directory'
|
||||
});
|
||||
promise = copy(
|
||||
{
|
||||
path: src,
|
||||
type: 'Directory',
|
||||
ignore: opts.ignore
|
||||
},
|
||||
{
|
||||
path: dst,
|
||||
mode: opts.mode,
|
||||
type: 'Directory'
|
||||
}
|
||||
);
|
||||
|
||||
if (opts.copyMode) {
|
||||
promise = promise.then(copyMode.bind(copyMode, src, dst));
|
||||
|
||||
@@ -10,41 +10,55 @@ function createLink(src, dst, type) {
|
||||
var dstDir = path.dirname(dst);
|
||||
|
||||
// Create directory
|
||||
return Q.nfcall(mkdirp, dstDir)
|
||||
// Check if source exists
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.stat, src)
|
||||
.fail(function (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
throw createError('Failed to create link to ' + path.basename(src), 'ENOENT', {
|
||||
details: src + ' does not exist or points to a non-existent file'
|
||||
return (
|
||||
Q.nfcall(mkdirp, dstDir)
|
||||
// Check if source exists
|
||||
.then(function() {
|
||||
return Q.nfcall(fs.stat, src).fail(function(error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
throw createError(
|
||||
'Failed to create link to ' + path.basename(src),
|
||||
'ENOENT',
|
||||
{
|
||||
details:
|
||||
src +
|
||||
' does not exist or points to a non-existent file'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
})
|
||||
// Create symlink
|
||||
.then(function(stat) {
|
||||
type = type || (stat.isDirectory() ? 'dir' : 'file');
|
||||
|
||||
throw error;
|
||||
});
|
||||
})
|
||||
// Create symlink
|
||||
.then(function (stat) {
|
||||
type = type || (stat.isDirectory() ? 'dir' : 'file');
|
||||
return Q.nfcall(fs.symlink, src, dst, type).fail(function(err) {
|
||||
if (!isWin || err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return Q.nfcall(fs.symlink, src, dst, type)
|
||||
.fail(function (err) {
|
||||
if (!isWin || err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Try with type "junction" on Windows
|
||||
// Junctions behave equally to true symlinks and can be created in
|
||||
// non elevated terminal (well, not always..)
|
||||
return Q.nfcall(fs.symlink, src, dst, 'junction')
|
||||
.fail(function (err) {
|
||||
throw createError('Unable to create link to ' + path.basename(src), err.code, {
|
||||
details: err.message.trim() + '\n\nTry running this command in an elevated terminal (run as root/administrator).'
|
||||
// Try with type "junction" on Windows
|
||||
// Junctions behave equally to true symlinks and can be created in
|
||||
// non elevated terminal (well, not always..)
|
||||
return Q.nfcall(fs.symlink, src, dst, 'junction').fail(
|
||||
function(err) {
|
||||
throw createError(
|
||||
'Unable to create link to ' +
|
||||
path.basename(src),
|
||||
err.code,
|
||||
{
|
||||
details:
|
||||
err.message.trim() +
|
||||
'\n\nTry running this command in an elevated terminal (run as root/administrator).'
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = createLink;
|
||||
|
||||
@@ -20,50 +20,53 @@ function download(url, file, options) {
|
||||
var deferred = Q.defer();
|
||||
var progressDelay = 8000;
|
||||
|
||||
options = mout.object.mixIn({
|
||||
retries: 5,
|
||||
factor: 2,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 35000,
|
||||
randomize: true,
|
||||
progressDelay: progressDelay,
|
||||
gzip: true
|
||||
}, options || {});
|
||||
options = mout.object.mixIn(
|
||||
{
|
||||
retries: 5,
|
||||
factor: 2,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 35000,
|
||||
randomize: true,
|
||||
progressDelay: progressDelay,
|
||||
gzip: true
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
|
||||
// Retry on network errors
|
||||
operation = retry.operation(options);
|
||||
|
||||
operation.attempt(function () {
|
||||
operation.attempt(function() {
|
||||
Q.fcall(fetch, url, file, options)
|
||||
.then(function (response) {
|
||||
deferred.resolve(response);
|
||||
})
|
||||
.progress(function (status) {
|
||||
deferred.notify(status);
|
||||
})
|
||||
.fail(function (error) {
|
||||
// Save timeout before retrying to report
|
||||
var timeout = operation._timeouts[0];
|
||||
.then(function(response) {
|
||||
deferred.resolve(response);
|
||||
})
|
||||
.progress(function(status) {
|
||||
deferred.notify(status);
|
||||
})
|
||||
.fail(function(error) {
|
||||
// Save timeout before retrying to report
|
||||
var timeout = operation._timeouts[0];
|
||||
|
||||
// Reject if error is not a network error
|
||||
if (errorCodes.indexOf(error.code) === -1) {
|
||||
return deferred.reject(error);
|
||||
}
|
||||
// Reject if error is not a network error
|
||||
if (errorCodes.indexOf(error.code) === -1) {
|
||||
return deferred.reject(error);
|
||||
}
|
||||
|
||||
// Next attempt will start reporting download progress immediately
|
||||
progressDelay = 0;
|
||||
// Next attempt will start reporting download progress immediately
|
||||
progressDelay = 0;
|
||||
|
||||
// This will schedule next retry or return false
|
||||
if (operation.retry(error)) {
|
||||
deferred.notify({
|
||||
retry: true,
|
||||
delay: timeout,
|
||||
error: error
|
||||
});
|
||||
} else {
|
||||
deferred.reject(error);
|
||||
}
|
||||
});
|
||||
// This will schedule next retry or return false
|
||||
if (operation.retry(error)) {
|
||||
deferred.notify({
|
||||
retry: true,
|
||||
delay: timeout,
|
||||
error: error
|
||||
});
|
||||
} else {
|
||||
deferred.reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
@@ -75,68 +78,75 @@ function fetch(url, file, options) {
|
||||
var contentLength;
|
||||
var bytesDownloaded = 0;
|
||||
|
||||
var reject = function (error) {
|
||||
var reject = function(error) {
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
var req = progress(request(url, options), {
|
||||
delay: options.progressDelay
|
||||
})
|
||||
.on('response', function (response) {
|
||||
contentLength = Number(response.headers['content-length']);
|
||||
.on('response', function(response) {
|
||||
contentLength = Number(response.headers['content-length']);
|
||||
|
||||
var status = response.statusCode;
|
||||
var status = response.statusCode;
|
||||
|
||||
if (status < 200 || status >= 300) {
|
||||
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
|
||||
}
|
||||
if (status < 200 || status >= 300) {
|
||||
return deferred.reject(
|
||||
createError('Status code of ' + status, 'EHTTP')
|
||||
);
|
||||
}
|
||||
|
||||
var writeStream = createWriteStream(file);
|
||||
var errored = false;
|
||||
var writeStream = createWriteStream(file);
|
||||
var errored = false;
|
||||
|
||||
// Change error listener so it cleans up writeStream before exiting
|
||||
req.removeListener('error', reject);
|
||||
req.on('error', function (error) {
|
||||
errored = true;
|
||||
destroy(req);
|
||||
destroy(writeStream);
|
||||
|
||||
// Wait for writeStream to cleanup after itself...
|
||||
// TODO: Maybe there's a better way?
|
||||
setTimeout(function () {
|
||||
deferred.reject(error);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
writeStream.on('finish', function () {
|
||||
if (!errored) {
|
||||
// Change error listener so it cleans up writeStream before exiting
|
||||
req.removeListener('error', reject);
|
||||
req.on('error', function(error) {
|
||||
errored = true;
|
||||
destroy(req);
|
||||
deferred.resolve(response);
|
||||
destroy(writeStream);
|
||||
|
||||
// Wait for writeStream to cleanup after itself...
|
||||
// TODO: Maybe there's a better way?
|
||||
setTimeout(function() {
|
||||
deferred.reject(error);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
writeStream.on('finish', function() {
|
||||
if (!errored) {
|
||||
destroy(req);
|
||||
deferred.resolve(response);
|
||||
}
|
||||
});
|
||||
|
||||
req.pipe(writeStream);
|
||||
})
|
||||
.on('data', function(data) {
|
||||
bytesDownloaded += data.length;
|
||||
})
|
||||
.on('progress', function(state) {
|
||||
deferred.notify(state);
|
||||
})
|
||||
.on('error', reject)
|
||||
.on('end', function() {
|
||||
// Check if the whole file was downloaded
|
||||
// In some unstable connections the ACK/FIN packet might be sent in the
|
||||
// middle of the download
|
||||
// See: https://github.com/joyent/node/issues/6143
|
||||
if (contentLength && bytesDownloaded < contentLength) {
|
||||
req.emit(
|
||||
'error',
|
||||
createError(
|
||||
'Transfer closed with ' +
|
||||
(contentLength - bytesDownloaded) +
|
||||
' bytes remaining to read',
|
||||
'EINCOMPLETE'
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
req.pipe(writeStream);
|
||||
})
|
||||
.on('data', function (data) {
|
||||
bytesDownloaded += data.length;
|
||||
})
|
||||
.on('progress', function (state) {
|
||||
deferred.notify(state);
|
||||
})
|
||||
.on('error', reject)
|
||||
.on('end', function () {
|
||||
// Check if the whole file was downloaded
|
||||
// In some unstable connections the ACK/FIN packet might be sent in the
|
||||
// middle of the download
|
||||
// See: https://github.com/joyent/node/issues/6143
|
||||
if (contentLength && bytesDownloaded < contentLength) {
|
||||
req.emit('error', createError(
|
||||
'Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read',
|
||||
'EINCOMPLETE'
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,13 +38,13 @@ function extractZip(archive, dst) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
new DecompressZip(archive)
|
||||
.on('error', deferred.reject)
|
||||
.on('extract', deferred.resolve.bind(deferred, dst))
|
||||
.extract({
|
||||
path: dst,
|
||||
follow: false, // Do not follow symlinks (#699)
|
||||
filter: filterSymlinks // Filter symlink files
|
||||
});
|
||||
.on('error', deferred.reject)
|
||||
.on('extract', deferred.resolve.bind(deferred, dst))
|
||||
.extract({
|
||||
path: dst,
|
||||
follow: false, // Do not follow symlinks (#699)
|
||||
filter: filterSymlinks // Filter symlink files
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@@ -54,22 +54,25 @@ function extractTar(archive, dst) {
|
||||
|
||||
var stream = fs.createReadStream(archive);
|
||||
|
||||
var reject = function (error) {
|
||||
var reject = function(error) {
|
||||
destroy(stream);
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
stream.on('error', reject)
|
||||
.pipe(tar.extract(dst, {
|
||||
ignore: isSymlink, // Filter symlink files
|
||||
dmode: 0555, // Ensure dirs are readable
|
||||
fmode: 0444 // Ensure files are readable
|
||||
}))
|
||||
.on('error', reject)
|
||||
.on('finish', function (result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
stream
|
||||
.on('error', reject)
|
||||
.pipe(
|
||||
tar.extract(dst, {
|
||||
ignore: isSymlink, // Filter symlink files
|
||||
dmode: 0555, // Ensure dirs are readable
|
||||
fmode: 0444 // Ensure files are readable
|
||||
})
|
||||
)
|
||||
.on('error', reject)
|
||||
.on('finish', function(result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@@ -79,24 +82,27 @@ function extractTarGz(archive, dst) {
|
||||
|
||||
var stream = fs.createReadStream(archive);
|
||||
|
||||
var reject = function (error) {
|
||||
var reject = function(error) {
|
||||
destroy(stream);
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
stream.on('error', reject)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(tar.extract(dst, {
|
||||
ignore: isSymlink, // Filter symlink files
|
||||
dmode: 0555, // Ensure dirs are readable
|
||||
fmode: 0444 // Ensure files are readable
|
||||
}))
|
||||
.on('error', reject)
|
||||
.on('finish', function (result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
stream
|
||||
.on('error', reject)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(
|
||||
tar.extract(dst, {
|
||||
ignore: isSymlink, // Filter symlink files
|
||||
dmode: 0555, // Ensure dirs are readable
|
||||
fmode: 0444 // Ensure files are readable
|
||||
})
|
||||
)
|
||||
.on('error', reject)
|
||||
.on('finish', function(result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@@ -106,25 +112,26 @@ function extractGz(archive, dst) {
|
||||
|
||||
var stream = fs.createReadStream(archive);
|
||||
|
||||
var reject = function (error) {
|
||||
var reject = function(error) {
|
||||
destroy(stream);
|
||||
deferred.reject(error);
|
||||
};
|
||||
stream.on('error', reject)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(createWriteStream(dst))
|
||||
.on('error', reject)
|
||||
.on('finish', function (result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
stream
|
||||
.on('error', reject)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(createWriteStream(dst))
|
||||
.on('error', reject)
|
||||
.on('finish', function(result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function isSymlink(entry) {
|
||||
return entry.type === 'SymbolicLink';
|
||||
function isSymlink(_, entry) {
|
||||
return entry.type === 'symlink';
|
||||
}
|
||||
|
||||
function filterSymlinks(entry) {
|
||||
@@ -136,7 +143,7 @@ function getExtractor(archive) {
|
||||
// This ensures that upper-cased extensions work
|
||||
archive = archive.toLowerCase();
|
||||
|
||||
var type = mout.array.find(extractorTypes, function (type) {
|
||||
var type = mout.array.find(extractorTypes, function(type) {
|
||||
return mout.string.endsWith(archive, type);
|
||||
});
|
||||
|
||||
@@ -144,8 +151,7 @@ function getExtractor(archive) {
|
||||
}
|
||||
|
||||
function isSingleDir(dir) {
|
||||
return Q.nfcall(fs.readdir, dir)
|
||||
.then(function (files) {
|
||||
return Q.nfcall(fs.readdir, dir).then(function(files) {
|
||||
var singleDir;
|
||||
|
||||
// Remove any OS specific files from the files array
|
||||
@@ -158,8 +164,7 @@ function isSingleDir(dir) {
|
||||
|
||||
singleDir = path.join(dir, files[0]);
|
||||
|
||||
return Q.nfcall(fs.stat, singleDir)
|
||||
.then(function (stat) {
|
||||
return Q.nfcall(fs.stat, singleDir).then(function(stat) {
|
||||
return stat.isDirectory() ? singleDir : false;
|
||||
});
|
||||
});
|
||||
@@ -167,19 +172,19 @@ function isSingleDir(dir) {
|
||||
|
||||
function moveDirectory(srcDir, destDir) {
|
||||
return Q.nfcall(fs.readdir, srcDir)
|
||||
.then(function (files) {
|
||||
var promises = files.map(function (file) {
|
||||
var src = path.join(srcDir, file);
|
||||
var dst = path.join(destDir, file);
|
||||
.then(function(files) {
|
||||
var promises = files.map(function(file) {
|
||||
var src = path.join(srcDir, file);
|
||||
var dst = path.join(destDir, file);
|
||||
|
||||
return Q.nfcall(fs.rename, src, dst);
|
||||
return Q.nfcall(fs.rename, src, dst);
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
.then(function() {
|
||||
return Q.nfcall(fs.rmdir, srcDir);
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.rmdir, srcDir);
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
@@ -209,57 +214,64 @@ function extract(src, dst, opts) {
|
||||
|
||||
// If extractor is null, then the archive type is unknown
|
||||
if (!extractor) {
|
||||
return Q.reject(createError('File ' + src + ' is not a known archive', 'ENOTARCHIVE'));
|
||||
return Q.reject(
|
||||
createError(
|
||||
'File ' + src + ' is not a known archive',
|
||||
'ENOTARCHIVE'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Extract to a temporary directory in case of file name clashes
|
||||
return Q.nfcall(tmp.dir, {
|
||||
template: dst + '-XXXXXX',
|
||||
mode: 0777 & ~process.umask()
|
||||
}).then(function (tempDir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return Array.isArray(tempDir) ? tempDir[0] : tempDir;
|
||||
}).then(function (tempDir) {
|
||||
|
||||
// Check archive file size
|
||||
promise = Q.nfcall(fs.stat, src)
|
||||
.then(function (stat) {
|
||||
if (stat.size <= 8) {
|
||||
throw createError('File ' + src + ' is an invalid archive', 'ENOTARCHIVE');
|
||||
}
|
||||
|
||||
// Extract archive
|
||||
return extractor(src, tempDir);
|
||||
});
|
||||
|
||||
// Remove archive
|
||||
if (!opts.keepArchive) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.unlink, src);
|
||||
});
|
||||
}
|
||||
|
||||
// Move contents from the temporary directory
|
||||
// If the contents are a single directory (and we're not preserving structure),
|
||||
// move its contents directly instead.
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return isSingleDir(tempDir);
|
||||
})
|
||||
.then(function(tempDir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return Array.isArray(tempDir) ? tempDir[0] : tempDir;
|
||||
})
|
||||
.then(function (singleDir) {
|
||||
if (singleDir && !opts.keepStructure) {
|
||||
return moveDirectory(singleDir, dst);
|
||||
} else {
|
||||
return moveDirectory(tempDir, dst);
|
||||
}
|
||||
});
|
||||
.then(function(tempDir) {
|
||||
// Check archive file size
|
||||
promise = Q.nfcall(fs.stat, src).then(function(stat) {
|
||||
if (stat.size <= 8) {
|
||||
throw createError(
|
||||
'File ' + src + ' is an invalid archive',
|
||||
'ENOTARCHIVE'
|
||||
);
|
||||
}
|
||||
|
||||
// Resolve promise to the dst dir
|
||||
return promise.then(function () {
|
||||
return dst;
|
||||
// Extract archive
|
||||
return extractor(src, tempDir);
|
||||
});
|
||||
|
||||
// Remove archive
|
||||
if (!opts.keepArchive) {
|
||||
promise = promise.then(function() {
|
||||
return Q.nfcall(fs.unlink, src);
|
||||
});
|
||||
}
|
||||
|
||||
// Move contents from the temporary directory
|
||||
// If the contents are a single directory (and we're not preserving structure),
|
||||
// move its contents directly instead.
|
||||
promise = promise
|
||||
.then(function() {
|
||||
return isSingleDir(tempDir);
|
||||
})
|
||||
.then(function(singleDir) {
|
||||
if (singleDir && !opts.keepStructure) {
|
||||
return moveDirectory(singleDir, dst);
|
||||
} else {
|
||||
return moveDirectory(tempDir, dst);
|
||||
}
|
||||
});
|
||||
|
||||
// Resolve promise to the dst dir
|
||||
return promise.then(function() {
|
||||
return dst;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = extract;
|
||||
|
||||
@@ -5,14 +5,14 @@ var readdirSync = fs.readdirSync.bind(fs);
|
||||
|
||||
module.exports = fs;
|
||||
|
||||
module.exports.readdir = function (dir, callback) {
|
||||
fs.stat(dir, function (err, stats) {
|
||||
module.exports.readdir = function(dir, callback) {
|
||||
fs.stat(dir, function(err, stats) {
|
||||
if (err) return callback(err);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return readdir(dir, callback);
|
||||
} else {
|
||||
var error = new Error('ENOTDIR, not a directory \'' + dir + '\'');
|
||||
var error = new Error("ENOTDIR, not a directory '" + dir + "'");
|
||||
error.code = 'ENOTDIR';
|
||||
error.path = dir;
|
||||
error.errono = -20;
|
||||
@@ -21,7 +21,7 @@ module.exports.readdir = function (dir, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.readdirSync = function (dir) {
|
||||
module.exports.readdirSync = function(dir) {
|
||||
var stats = fs.statSync(dir);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
|
||||
@@ -13,31 +13,43 @@ function readJson(file, options) {
|
||||
options = options || {};
|
||||
|
||||
// Read
|
||||
return Q.nfcall(bowerJson.read, file, options)
|
||||
.spread(function (json, jsonFile) {
|
||||
var deprecated;
|
||||
return Q.nfcall(bowerJson.read, file, options).spread(
|
||||
function(json, jsonFile) {
|
||||
var deprecated;
|
||||
|
||||
jsonFile = path.basename(jsonFile);
|
||||
deprecated = jsonFile === 'component.json' ? jsonFile : false;
|
||||
if (options.logger) {
|
||||
var issues = bowerJson.getIssues(json);
|
||||
if (issues.warnings.length > 0) {
|
||||
options.logger.warn('invalid-meta', 'for:' + jsonFile);
|
||||
}
|
||||
issues.warnings.forEach(function(warning) {
|
||||
options.logger.warn('invalid-meta', warning);
|
||||
});
|
||||
}
|
||||
|
||||
return [json, deprecated, false];
|
||||
}, function (err) {
|
||||
// No json file was found, assume one
|
||||
if (err.code === 'ENOENT' && options.assume) {
|
||||
return [bowerJson.parse(options.assume, options), false, true];
|
||||
jsonFile = path.basename(jsonFile);
|
||||
deprecated = jsonFile === 'component.json' ? jsonFile : false;
|
||||
|
||||
return [json, deprecated, false];
|
||||
},
|
||||
function(err) {
|
||||
// No json file was found, assume one
|
||||
if (err.code === 'ENOENT' && options.assume) {
|
||||
return [bowerJson.parse(options.assume, options), false, true];
|
||||
}
|
||||
|
||||
err.details = err.message;
|
||||
|
||||
if (err.file) {
|
||||
err.message = 'Failed to read ' + err.file;
|
||||
err.data = { filename: err.file };
|
||||
} else {
|
||||
err.message = 'Failed to read json from ' + file;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
err.details = err.message;
|
||||
|
||||
if (err.file) {
|
||||
err.message = 'Failed to read ' + err.file;
|
||||
err.data = { filename: err.file };
|
||||
} else {
|
||||
err.message = 'Failed to read json from ' + file;
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = readJson;
|
||||
|
||||
@@ -2,7 +2,7 @@ var path = require('path');
|
||||
var isPathAbsolute = require('./isPathAbsolute');
|
||||
|
||||
function relativeToBaseDir(baseDir) {
|
||||
return function (filePath) {
|
||||
return function(filePath) {
|
||||
if (isPathAbsolute(filePath)) {
|
||||
return path.resolve(filePath);
|
||||
} else {
|
||||
|
||||
@@ -14,7 +14,7 @@ function removeIgnores(dir, meta) {
|
||||
// Don't ignore main files
|
||||
nonIgnored = nonIgnored.concat(meta.main || []);
|
||||
|
||||
nonIgnored = nonIgnored.map(function (file) {
|
||||
nonIgnored = nonIgnored.map(function(file) {
|
||||
return path.join(dir, file);
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ function removeIgnores(dir, meta) {
|
||||
|
||||
// Monkey patch applyIgnores such that we get hold of all ignored files
|
||||
applyIgnores = reader.applyIgnores;
|
||||
reader.applyIgnores = function (entry) {
|
||||
reader.applyIgnores = function(entry) {
|
||||
var ret = applyIgnores.apply(this, arguments);
|
||||
|
||||
if (!ret) {
|
||||
@@ -38,28 +38,27 @@ function removeIgnores(dir, meta) {
|
||||
};
|
||||
|
||||
reader
|
||||
.on('child', function (entry) {
|
||||
nonIgnored.push(entry.path);
|
||||
})
|
||||
.on('error', deferred.reject)
|
||||
.on('end', function () {
|
||||
var promises;
|
||||
.on('child', function(entry) {
|
||||
nonIgnored.push(entry.path);
|
||||
})
|
||||
.on('error', deferred.reject)
|
||||
.on('end', function() {
|
||||
var promises;
|
||||
|
||||
// Ensure that we are not ignoring files that should not be ignored!
|
||||
ignored = mout.array.unique(ignored);
|
||||
ignored = ignored.filter(function (file) {
|
||||
return nonIgnored.indexOf(file) === -1;
|
||||
// Ensure that we are not ignoring files that should not be ignored!
|
||||
ignored = mout.array.unique(ignored);
|
||||
ignored = ignored.filter(function(file) {
|
||||
return nonIgnored.indexOf(file) === -1;
|
||||
});
|
||||
|
||||
// Delete all the ignored files
|
||||
promises = ignored.map(function(file) {
|
||||
return Q.nfcall(rimraf, file);
|
||||
});
|
||||
|
||||
return Q.all(promises).then(deferred.resolve, deferred.reject);
|
||||
});
|
||||
|
||||
// Delete all the ignored files
|
||||
promises = ignored.map(function (file) {
|
||||
return Q.nfcall(rimraf, file);
|
||||
});
|
||||
|
||||
return Q.all(promises)
|
||||
.then(deferred.resolve, deferred.reject);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ var resolve = require('resolve');
|
||||
function startsWith(string, searchString, position) {
|
||||
position = position || 0;
|
||||
return string.substr(position, searchString.length) === searchString;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function (id, options) {
|
||||
module.exports = function(id, options) {
|
||||
var resolvedPath;
|
||||
|
||||
var cwd = (options || {}).cwd || process.cwd();
|
||||
@@ -19,4 +19,4 @@ module.exports = function (id, options) {
|
||||
}
|
||||
|
||||
return resolvedPath;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,15 +2,15 @@ var rimraf = require('rimraf');
|
||||
var chmodr = require('chmodr');
|
||||
var fs = require('./fs');
|
||||
|
||||
module.exports = function (dir, callback) {
|
||||
var checkAndRetry = function (e) {
|
||||
fs.lstat(dir, function (err, stats) {
|
||||
module.exports = function(dir, callback) {
|
||||
var checkAndRetry = function(e) {
|
||||
fs.lstat(dir, function(err, stats) {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') return callback();
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
chmodr(dir, 0777, function (err) {
|
||||
chmodr(dir, 0777, function(err) {
|
||||
if (err) return callback(e);
|
||||
rimraf(dir, callback);
|
||||
});
|
||||
@@ -24,8 +24,8 @@ module.exports = function (dir, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.sync = function (dir) {
|
||||
var checkAndRetry = function () {
|
||||
module.exports.sync = function(dir) {
|
||||
var checkAndRetry = function() {
|
||||
try {
|
||||
fs.lstatSync(dir);
|
||||
chmodr.sync(dir, 0777);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
'use strict';
|
||||
var isRoot = require('is-root');
|
||||
var createError = require('./createError');
|
||||
|
||||
@@ -12,18 +11,23 @@ function rootCheck(options, config) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorMsg = 'Since bower is a user command, there is no need to execute it with \
|
||||
errorMsg =
|
||||
'Since bower is a user command, there is no need to execute it with \
|
||||
superuser permissions.\nIf you\'re having permission errors when using bower without \
|
||||
sudo, please spend a few minutes learning more about how your system should work and \
|
||||
make any necessary repairs.\n\n\
|
||||
http://www.joyent.com/blog/installing-node-and-npm\n\
|
||||
https://gist.github.com/isaacs/579814\n\n\
|
||||
You can however run a command with sudo using --allow-root option';
|
||||
You can however run a command with sudo using "--allow-root" option';
|
||||
|
||||
if (isRoot()) {
|
||||
var cli = require('./cli');
|
||||
renderer = cli.getRenderer('', false, config);
|
||||
renderer.error(createError('Cannot be run with sudo', 'ESUDO', { details : errorMsg }));
|
||||
renderer.error(
|
||||
createError('Cannot be run with sudo', 'ESUDO', {
|
||||
details: errorMsg
|
||||
})
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ function maxSatisfying(versions, range, strictMatch) {
|
||||
var filteredVersions;
|
||||
|
||||
// Filter only valid versions, since semver.maxSatisfying() throws an error
|
||||
versions = versions.filter(function (version) {
|
||||
versions = versions.filter(function(version) {
|
||||
return semver.valid(version);
|
||||
});
|
||||
|
||||
// Exact version & range match
|
||||
if (semver.valid(range)) {
|
||||
version = mout.array.find(versions, function (version) {
|
||||
version = mout.array.find(versions, function(version) {
|
||||
return version === range;
|
||||
});
|
||||
if (version) {
|
||||
@@ -25,7 +25,7 @@ function maxSatisfying(versions, range, strictMatch) {
|
||||
// When strict match is enabled give priority to non-pre-releases
|
||||
// We do this by filtering every pre-release version
|
||||
if (strictMatch) {
|
||||
filteredVersions = versions.map(function (version) {
|
||||
filteredVersions = versions.map(function(version) {
|
||||
return !isPreRelease(version) ? version : null;
|
||||
});
|
||||
|
||||
@@ -57,7 +57,10 @@ function clean(version) {
|
||||
}
|
||||
|
||||
// Keep builds!
|
||||
return parsed.version + (parsed.build.length ? '+' + parsed.build.join('.') : '');
|
||||
return (
|
||||
parsed.version +
|
||||
(parsed.build.length ? '+' + parsed.build.join('.') : '')
|
||||
);
|
||||
}
|
||||
|
||||
function isPreRelease(version) {
|
||||
|
||||
@@ -8,7 +8,7 @@ var templatesDir = path.resolve(__dirname, '../templates');
|
||||
var cache = {};
|
||||
|
||||
// Register helpers
|
||||
mout.object.forOwn(helpers, function (register) {
|
||||
mout.object.forOwn(helpers, function(register) {
|
||||
register(Handlebars);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
var version = require('../version');
|
||||
|
||||
module.exports = 'node/' + process.version + ' ' + process.platform + ' ' + process.arch + ' ' + ';Bower ' + version;
|
||||
module.exports =
|
||||
'node/' +
|
||||
process.version +
|
||||
' ' +
|
||||
process.platform +
|
||||
' ' +
|
||||
process.arch +
|
||||
' ' +
|
||||
';Bower ' +
|
||||
version;
|
||||
|
||||
@@ -5,20 +5,18 @@ function validLink(file) {
|
||||
// Ensures that a file is a symlink that points
|
||||
// to a valid file
|
||||
return Q.nfcall(fs.lstat, file)
|
||||
.then(function (lstat) {
|
||||
if (!lstat.isSymbolicLink()) {
|
||||
return [false];
|
||||
}
|
||||
.then(function(lstat) {
|
||||
if (!lstat.isSymbolicLink()) {
|
||||
return [false];
|
||||
}
|
||||
|
||||
return Q.nfcall(fs.stat, file)
|
||||
.then(function (stat) {
|
||||
return [stat];
|
||||
return Q.nfcall(fs.stat, file).then(function(stat) {
|
||||
return [stat];
|
||||
});
|
||||
})
|
||||
.fail(function(err) {
|
||||
return [false, err];
|
||||
});
|
||||
})
|
||||
.fail(function (err) {
|
||||
return [false, err];
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = validLink;
|
||||
|
||||
|
||||
50
package.json
50
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bower",
|
||||
"version": "1.7.8",
|
||||
"version": "1.8.8",
|
||||
"description": "The browser package manager",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
@@ -17,16 +17,16 @@
|
||||
"dependencies": {
|
||||
"abbrev": "^1.0.5",
|
||||
"archy": "1.0.0",
|
||||
"bower-config": "^1.3.1",
|
||||
"bower-config": "^1.4.1",
|
||||
"bower-endpoint-parser": "^0.2.2",
|
||||
"bower-json": "^0.8.0",
|
||||
"bower-json": "^0.8.1",
|
||||
"bower-logger": "^0.2.2",
|
||||
"bower-registry-client": "^1.0.0",
|
||||
"cardinal": "0.4.4",
|
||||
"chalk": "^1.0.0",
|
||||
"chmodr": "^1.0.2",
|
||||
"configstore": "^2.0.0",
|
||||
"decompress-zip": "^0.2.1",
|
||||
"decompress-zip": "^0.2.2",
|
||||
"destroy": "^1.0.3",
|
||||
"findup-sync": "^0.3.0",
|
||||
"fs-write-stream-atomic": "1.0.8",
|
||||
@@ -67,31 +67,35 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"arr-diff": "^2.0.0",
|
||||
"chai": "^1.10.0",
|
||||
"coveralls": "^2.11.2",
|
||||
"chai": "^3.5.0",
|
||||
"coveralls": "^2.11.9",
|
||||
"eslint": "^2.0.0",
|
||||
"expect.js": "^0.3.1",
|
||||
"grunt": "1.0.0-rc1",
|
||||
"grunt-cli": "^1.1.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-eslint": "^18.0.0",
|
||||
"grunt-exec": "sheerun/grunt-exec",
|
||||
"grunt-simple-mocha": "^0.4.1",
|
||||
"husky": "^0.14.3",
|
||||
"in-publish": "^2.0.0",
|
||||
"istanbul": "^0.3.5",
|
||||
"load-grunt-tasks": "^2.0.0",
|
||||
"mocha": "^2.1.0",
|
||||
"istanbul": "^0.4.3",
|
||||
"lint-staged": "^9.5.0",
|
||||
"mocha": "^2.5.3",
|
||||
"multiline": "^1.0.2",
|
||||
"nock": "^3.1.0",
|
||||
"node-uuid": "^1.4.2",
|
||||
"proxyquire": "^1.3.0",
|
||||
"spawn-sync": "1.0.13",
|
||||
"nock": "^11.7.0",
|
||||
"nock-legacy": "npm:nock@9.2.3",
|
||||
"node-uuid": "^1.4.7",
|
||||
"prettier": "^1.19.1",
|
||||
"proxyquire": "^1.7.9",
|
||||
"spawn-sync": "1.0.15",
|
||||
"wrench": "^1.5.8"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"ci": "grunt travis",
|
||||
"coveralls": "coveralls",
|
||||
"prepublish": "in-publish && echo 'You need to use \"grunt publish\" to publish bower' && false || not-in-publish"
|
||||
"lint": "eslint .",
|
||||
"test": "node test/packages.js && node test/packages-svn.js && mocha --timeout 15000 --reporter spec",
|
||||
"prepublishOnly": "in-publish && echo 'You need to use \"node publish.js\" to publish bower' && false || not-in-publish",
|
||||
"format": "prettier --write --single-quote --tab-width 4 '**/*.js'",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"prettier --single-quote --tab-width 4"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
|
||||
12
packages/bower-config/.editorconfig
Normal file
12
packages/bower-config/.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
7
packages/bower-config/.gitignore
vendored
Normal file
7
packages/bower-config/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
test/assets/github-test-package
|
||||
test/assets/github-test-package-copy
|
||||
test/assets/temp
|
||||
test/reports
|
||||
103
packages/bower-config/CHANGELOG.md
Normal file
103
packages/bower-config/CHANGELOG.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Changelog
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- Change default shorthand resolver from git:// to https://
|
||||
|
||||
## 1.3.1
|
||||
|
||||
- Ignore hook scripts for environment variable expansion
|
||||
|
||||
## 1.3.0 - 2015-12-07
|
||||
|
||||
- Allow the use of environment variables in .bowerrc. Fixes [#41](https://github.com/bower/config/issues/41)
|
||||
- Loads the .bowerrc file from the cwd specified on the command line. Fixes [bower/bower#1993](https://github.com/bower/bower/issues/1993)
|
||||
- Allwow for array notation in ENV variables [#44](https://github.com/bower/config/issues/44)
|
||||
|
||||
## 1.2.3 - 2015-11-27
|
||||
|
||||
- Restores env variables if they are undefined at the beginning
|
||||
- Handles default setting for config.ca. Together with [bower/bower PR #1972](https://github.com/bower/bower/pull/1972), fixes downloading with `strict-ssl` using custom CA
|
||||
- Displays an error message if .bowerrc is a directory instead of file. Fixes [bower/bower#2022](https://github.com/bower/bower/issues/2022)
|
||||
|
||||
## 1.2.2 - 2015-10-16
|
||||
- Fixes registry configurartion expanding [bower/bower#1950](https://github.com/bower/bower/issues/1950)
|
||||
|
||||
## 1.2.1 - 2015-10-15
|
||||
- Fixes case insenstivity HTTP_PROXY setting issue on Windows
|
||||
|
||||
## 1.2.0 - 2015-09-28
|
||||
- Prevent defaulting cwd to process.cwd()
|
||||
|
||||
## 1.1.2 - 2015-09-27
|
||||
- Performs only camel case normalisation before merging
|
||||
|
||||
## 1.1.1 - 2015-09-27
|
||||
- Fix: Merge extra options after camel-case normalisation, instead of before it
|
||||
|
||||
## 1.1.0 - 2015-09-27
|
||||
- Allow for overwriting options with .load(overwrites) / .read(cwd, overwrites)
|
||||
|
||||
## 1.0.1 - 2015-09-27
|
||||
- Update dependencies and relax "mout" version range
|
||||
- Most significant changes:
|
||||
- graceful-fs updated from 2.x version to 4.x
|
||||
- osenv updated to from 0.0.x to 0.1.x, [tmp location changed](https://github.com/npm/osenv/commit/d6eddbc026538b09026b1dbd60fbc081a8c67e03)
|
||||
|
||||
## 1.0.0 - 2015-09-27
|
||||
- Support for no-proxy configuration variable
|
||||
- Overwrite HTTP_PROXY, HTTPS_PROXY, and NO_PROXY env variables in load method
|
||||
- Normalise paths to certificates with contents of them, [#28](https://github.com/bower/config/pull/28)
|
||||
|
||||
## 0.6.1 - 2015-04-1
|
||||
- Fixes merging .bowerrc files upward directory tree. [#25](https://github.com/bower/config/issues/25)
|
||||
|
||||
## 0.6.0 - 2015-03-30
|
||||
- Merge .bowerrc files upward directory tree (fixes [bower/bower#1689](https://github.com/bower/bower/issues/1689)) [#24](https://github.com/bower/config/pull/24)
|
||||
- Allow NPM config variables (resolves [bower/bower#1711](https://github.com/bower/bower/issues/1711)) [#23](https://github.com/bower/config/pull/23)
|
||||
|
||||
## 0.5.2 - 2014-06-09
|
||||
- Fixes downloading of bower modules with ignores when .bowerrc is overridden with a relative tmp path. [#17](https://github.com/bower/config/issues/17) [bower/bower#1299](https://github.com/bower/bower/issues/1299)
|
||||
|
||||
## 0.5.1 - 2014-05-21
|
||||
- [perf] Uses the same mout version as bower
|
||||
- [perf] Uses only relevant parts of mout. Related [bower/bower#1134](https://github.com/bower/bower/pull/1134)
|
||||
|
||||
## 0.5.0 - 2013-08-30
|
||||
- Adds a DEFAULT_REGISTRY key to the Config class that exposes the bower registry UR. [#6](https://github.com/bower/config/issues/6)
|
||||
|
||||
## 0.4.5 - 2013-08-28
|
||||
- Fixes crashing when home is not set
|
||||
|
||||
## 0.4.4 - 2013-08-21
|
||||
- Supports nested environment variables [#8](https://github.com/bower/config/issues/8)
|
||||
|
||||
## 0.4.3 - 2013-08-19
|
||||
- Improvement in argv.config parsing
|
||||
|
||||
## 0.4.2 - 2013-08-18
|
||||
- Sets interative to auto
|
||||
|
||||
## 0.4.1 - 2013-08-18
|
||||
- Generates a fake user instead of using 'unknown'
|
||||
|
||||
## 0.4.0 - 2013-08-16
|
||||
- Suffixes temp folder with the user and 'bower'
|
||||
|
||||
## 0.3.5 - 2013-08-14
|
||||
- Casts buffer to string
|
||||
|
||||
## 0.3.4 - 2013-08-11
|
||||
- Empty .bowerrc files no longer throw an error.
|
||||
|
||||
## 0.3.3 - 2013-08-11
|
||||
- Changes git folder to empty (was not being used anyway)
|
||||
|
||||
## 0.3.2 - 2013-08-07
|
||||
- Uses a known user agent by default when a proxy.
|
||||
|
||||
## 0.3.1 - 2013-08-06
|
||||
- Fixes Typo
|
||||
|
||||
## 0.3.0 - 2013-08-06
|
||||
- Appends the username when using the temporary folder.
|
||||
19
packages/bower-config/LICENSE
Normal file
19
packages/bower-config/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2012 Twitter and other contributors
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
76
packages/bower-config/README.md
Normal file
76
packages/bower-config/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# bower-config
|
||||
|
||||
> The Bower config (`.bowerrc`) reader and writer.
|
||||
|
||||
[Bower](http://bower.io/) can be configured using JSON in a `.bowerrc` file. For example:
|
||||
|
||||
{
|
||||
"directory": "app/components/",
|
||||
"timeout": 120000,
|
||||
"registry": {
|
||||
"search": [
|
||||
"http://localhost:8000",
|
||||
"https://registry.bower.io"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
View the complete [.bowerrc specification](http://bower.io/docs/config/#bowerrc-specification) on the website for more details. Both the `bower.json` and `.bowerrc` specifications are maintained at [github.com/bower/spec](https://github.com/bower/spec).
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
$ npm install --save bower-config
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
#### .load(overwrites)
|
||||
|
||||
Loads the bower configuration from the configuration files.
|
||||
|
||||
Configuration is overwritten (after camelcase normalisation) with `overwrites` argument.
|
||||
|
||||
This method overwrites following environment variables:
|
||||
|
||||
- `HTTP_PROXY` with `proxy` configuration variable
|
||||
- `HTTPS_PROXY` with `https-proxy` configuration variable
|
||||
- `NO_PROXY` with `no-proxy` configuration variable
|
||||
|
||||
It also clears `http_proxy`, `https_proxy`, and `no_proxy` environment variables.
|
||||
|
||||
To restore those variables you can use `restore` method.
|
||||
|
||||
#### restore()
|
||||
|
||||
Restores environment variables overwritten by `.load` method.
|
||||
|
||||
#### .toObject()
|
||||
|
||||
Returns a deep copy of the underlying configuration object.
|
||||
The returned configuration is normalised.
|
||||
The object keys will be camelCase.
|
||||
|
||||
|
||||
#### #create(cwd)
|
||||
|
||||
Obtains a instance where `cwd` is the current working directory (defaults to `process.cwd`);
|
||||
|
||||
```js
|
||||
var config = require('bower-config').create();
|
||||
// You can also specify a working directory
|
||||
var config2 = require('bower-config').create('./some/path');
|
||||
```
|
||||
|
||||
#### #read(cwd, overrides)
|
||||
|
||||
Alias for:
|
||||
|
||||
```js
|
||||
var configObject = (new Config(cwd)).load(overrides).toJson();
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
|
||||
113
packages/bower-config/lib/Config.js
Normal file
113
packages/bower-config/lib/Config.js
Normal file
@@ -0,0 +1,113 @@
|
||||
var lang = require('mout/lang');
|
||||
var object = require('mout/object');
|
||||
var rc = require('./util/rc');
|
||||
var expand = require('./util/expand');
|
||||
var EnvProxy = require('./util/proxy');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
function Config(cwd) {
|
||||
this._cwd = cwd;
|
||||
this._proxy = new EnvProxy();
|
||||
this._config = {};
|
||||
}
|
||||
|
||||
Config.prototype.load = function(overwrites) {
|
||||
this._config = rc('bower', this._cwd);
|
||||
|
||||
this._config = object.merge(
|
||||
expand(this._config || {}),
|
||||
expand(overwrites || {})
|
||||
);
|
||||
|
||||
this._config = normalise(this._config);
|
||||
|
||||
this._proxy.set(this._config);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Config.prototype.restore = function() {
|
||||
this._proxy.restore();
|
||||
};
|
||||
|
||||
function readCertFile(path) {
|
||||
path = path || '';
|
||||
|
||||
var sep = '-----END CERTIFICATE-----';
|
||||
|
||||
var certificates;
|
||||
|
||||
if (path.indexOf(sep) === -1) {
|
||||
certificates = fs.readFileSync(path, { encoding: 'utf8' });
|
||||
} else {
|
||||
certificates = path;
|
||||
}
|
||||
|
||||
return certificates
|
||||
.split(sep)
|
||||
.filter(function(s) {
|
||||
return !s.match(/^\s*$/);
|
||||
})
|
||||
.map(function(s) {
|
||||
return s + sep;
|
||||
});
|
||||
}
|
||||
|
||||
function loadCAs(caConfig) {
|
||||
// If a ca file path has been specified, expand that here to the file's
|
||||
// contents. As a user can specify these individually, we must load them
|
||||
// one by one.
|
||||
for (var p in caConfig) {
|
||||
if (caConfig.hasOwnProperty(p)) {
|
||||
var prop = caConfig[p];
|
||||
if (Array.isArray(prop)) {
|
||||
caConfig[p] = prop.map(function(s) {
|
||||
return readCertFile(s);
|
||||
});
|
||||
} else if (prop) {
|
||||
caConfig[p] = readCertFile(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config.prototype.toObject = function() {
|
||||
return lang.deepClone(this._config);
|
||||
};
|
||||
|
||||
Config.create = function(cwd) {
|
||||
return new Config(cwd);
|
||||
};
|
||||
|
||||
Config.read = function(cwd, overrides) {
|
||||
var config = Config.create(cwd);
|
||||
return config.load(overrides).toObject();
|
||||
};
|
||||
|
||||
function normalise(config) {
|
||||
config = expand(config);
|
||||
|
||||
// Some backwards compatible things..
|
||||
if (config.shorthandResolver) {
|
||||
config.shorthandResolver = config.shorthandResolver
|
||||
.replace(/\{\{\{/g, '{{')
|
||||
.replace(/\}\}\}/g, '}}');
|
||||
}
|
||||
|
||||
// Ensure that every registry endpoint does not end with /
|
||||
config.registry.search = config.registry.search.map(function(url) {
|
||||
return url.replace(/\/+$/, '');
|
||||
});
|
||||
config.registry.register = config.registry.register.replace(/\/+$/, '');
|
||||
config.registry.publish = config.registry.publish.replace(/\/+$/, '');
|
||||
config.tmp = path.resolve(config.tmp);
|
||||
|
||||
loadCAs(config.ca);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
Config.DEFAULT_REGISTRY = require('./util/defaults').registry;
|
||||
|
||||
module.exports = Config;
|
||||
46
packages/bower-config/lib/util/defaults.js
Normal file
46
packages/bower-config/lib/util/defaults.js
Normal file
@@ -0,0 +1,46 @@
|
||||
var path = require('path');
|
||||
var paths = require('./paths');
|
||||
|
||||
// Guess proxy defined in the env
|
||||
var proxy = process.env.HTTP_PROXY || process.env.http_proxy || null;
|
||||
|
||||
var httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy || proxy;
|
||||
|
||||
var noProxy = process.env.NO_PROXY || process.env.no_proxy;
|
||||
|
||||
// Use a well known user agent (in this case, curl) when using a proxy,
|
||||
// to avoid potential filtering on many corporate proxies with blank or unknown agents
|
||||
var userAgent =
|
||||
!proxy && !httpsProxy
|
||||
? 'node/' +
|
||||
process.version +
|
||||
' ' +
|
||||
process.platform +
|
||||
' ' +
|
||||
process.arch
|
||||
: 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5';
|
||||
|
||||
var defaults = {
|
||||
directory: 'bower_components',
|
||||
registry: 'https://registry.bower.io',
|
||||
'shorthand-resolver': 'https://github.com/{{owner}}/{{package}}.git',
|
||||
tmp: paths.tmp,
|
||||
proxy: proxy,
|
||||
'https-proxy': httpsProxy,
|
||||
'no-proxy': noProxy,
|
||||
timeout: 30000,
|
||||
ca: { search: [] },
|
||||
'strict-ssl': true,
|
||||
'user-agent': userAgent,
|
||||
color: true,
|
||||
interactive: null,
|
||||
storage: {
|
||||
packages: path.join(paths.cache, 'packages'),
|
||||
links: path.join(paths.data, 'links'),
|
||||
completion: path.join(paths.data, 'completion'),
|
||||
registry: path.join(paths.cache, 'registry'),
|
||||
empty: path.join(paths.data, 'empty') // Empty dir, used in GIT_TEMPLATE_DIR among others
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = defaults;
|
||||
130
packages/bower-config/lib/util/expand.js
Normal file
130
packages/bower-config/lib/util/expand.js
Normal file
@@ -0,0 +1,130 @@
|
||||
var object = require('mout/object');
|
||||
var lang = require('mout/lang');
|
||||
var string = require('mout/string');
|
||||
|
||||
function camelCase(config) {
|
||||
var camelCased = {};
|
||||
|
||||
// Camel case
|
||||
object.forOwn(config, function(value, key) {
|
||||
// Ignore null values
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
key = string.camelCase(key.replace(/_/g, '-'));
|
||||
camelCased[key] = lang.isPlainObject(value) ? camelCase(value) : value;
|
||||
});
|
||||
|
||||
return camelCased;
|
||||
}
|
||||
|
||||
// Function to replace ${VAR} - style variables
|
||||
// with values set in the environment
|
||||
// This function expects to be passed a string
|
||||
function doEnvReplaceStr(f) {
|
||||
// Un-tildify
|
||||
var untildify = require('untildify');
|
||||
f = untildify(f);
|
||||
|
||||
// replace any ${ENV} values with the appropriate environ.
|
||||
var envExpr = /(\\*)\$\{([^}]+)\}/g;
|
||||
return f.replace(envExpr, function(orig, esc, name) {
|
||||
esc = esc.length && esc.length % 2;
|
||||
if (esc) return orig;
|
||||
if (undefined === process.env[name]) {
|
||||
throw new Error(
|
||||
'Environment variable used in .bowerrc is not defined: ' + orig
|
||||
);
|
||||
}
|
||||
|
||||
return process.env[name];
|
||||
});
|
||||
}
|
||||
|
||||
function envReplace(config) {
|
||||
var envReplaced = {};
|
||||
if (lang.isArray(config)) {
|
||||
envReplaced = [];
|
||||
}
|
||||
|
||||
object.forOwn(config, function(value, key) {
|
||||
// Ignore null values
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore 'scripts'
|
||||
// These hooks run within the shell
|
||||
// environment variable expansion is not required
|
||||
if (key === 'scripts' && lang.isPlainObject(value)) {
|
||||
envReplaced[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform variable replacements based on var type
|
||||
if (lang.isPlainObject(value)) {
|
||||
envReplaced[key] = envReplace(value);
|
||||
} else if (lang.isArray(value)) {
|
||||
envReplaced[key] = envReplace(value);
|
||||
} else if (lang.isString(value)) {
|
||||
envReplaced[key] = doEnvReplaceStr(value);
|
||||
} else {
|
||||
envReplaced[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return envReplaced;
|
||||
}
|
||||
|
||||
function expand(config) {
|
||||
config = camelCase(config);
|
||||
config = envReplace(config);
|
||||
|
||||
if (typeof config.registry === 'string') {
|
||||
config.registry = {
|
||||
default: config.registry,
|
||||
search: [config.registry],
|
||||
register: config.registry,
|
||||
publish: config.registry
|
||||
};
|
||||
} else if (typeof config.registry === 'object') {
|
||||
config.registry.default =
|
||||
config.registry.default || 'https://registry.bower.io';
|
||||
|
||||
config.registry = {
|
||||
default: config.registry.default,
|
||||
search: config.registry.search || config.registry.default,
|
||||
register: config.registry.register || config.registry.default,
|
||||
publish: config.registry.publish || config.registry.default
|
||||
};
|
||||
|
||||
if (config.registry.search && !Array.isArray(config.registry.search)) {
|
||||
config.registry.search = [config.registry.search];
|
||||
}
|
||||
}
|
||||
|
||||
// CA
|
||||
if (typeof config.ca === 'string') {
|
||||
config.ca = {
|
||||
default: config.ca,
|
||||
search: [config.ca],
|
||||
register: config.ca,
|
||||
publish: config.ca
|
||||
};
|
||||
} else if (typeof config.ca === 'object') {
|
||||
if (config.ca.search && !Array.isArray(config.ca.search)) {
|
||||
config.ca.search = [config.ca.search];
|
||||
}
|
||||
|
||||
if (config.ca.default) {
|
||||
config.ca.search = config.ca.search || config.ca.default;
|
||||
config.ca.register = config.ca.register || config.ca.default;
|
||||
config.ca.publish = config.ca.publish || config.ca.default;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = expand;
|
||||
52
packages/bower-config/lib/util/paths.js
Normal file
52
packages/bower-config/lib/util/paths.js
Normal file
@@ -0,0 +1,52 @@
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var osenv = require('osenv');
|
||||
var crypto = require('crypto');
|
||||
|
||||
function generateFakeUser() {
|
||||
var uid =
|
||||
process.pid +
|
||||
'-' +
|
||||
Date.now() +
|
||||
'-' +
|
||||
Math.floor(Math.random() * 1000000);
|
||||
return crypto
|
||||
.createHash('md5')
|
||||
.update(uid)
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
// Assume XDG defaults
|
||||
// See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
var paths = {
|
||||
config: process.env.XDG_CONFIG_HOME,
|
||||
data: process.env.XDG_DATA_HOME,
|
||||
cache: process.env.XDG_CACHE_HOME
|
||||
};
|
||||
|
||||
// Guess some needed properties based on the user OS
|
||||
var user = (osenv.user() || generateFakeUser()).replace(/\\/g, '-');
|
||||
var tmp = path.join(os.tmpdir ? os.tmpdir() : os.tmpDir(), user);
|
||||
var home = osenv.home();
|
||||
var base;
|
||||
|
||||
// Fallbacks for windows
|
||||
if (process.platform === 'win32') {
|
||||
base = path.resolve(process.env.LOCALAPPDATA || home || tmp);
|
||||
base = path.join(base, 'bower');
|
||||
|
||||
paths.config = paths.config || path.join(base, 'config');
|
||||
paths.data = paths.data || path.join(base, 'data');
|
||||
paths.cache = paths.cache || path.join(base, 'cache');
|
||||
// Fallbacks for other operating systems
|
||||
} else {
|
||||
base = path.resolve(home || tmp);
|
||||
|
||||
paths.config = paths.config || path.join(base, '.config/bower');
|
||||
paths.data = paths.data || path.join(base, '.local/share/bower');
|
||||
paths.cache = paths.cache || path.join(base, '.cache/bower');
|
||||
}
|
||||
|
||||
paths.tmp = path.resolve(path.join(tmp, 'bower'));
|
||||
|
||||
module.exports = paths;
|
||||
79
packages/bower-config/lib/util/proxy.js
Normal file
79
packages/bower-config/lib/util/proxy.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// EnvProxy uses the proxy vaiables passed to it in set and sets the
|
||||
// process.env uppercase proxy variables to them with the ability
|
||||
// to restore the original values later
|
||||
var EnvProxy = function() {
|
||||
this.restoreFrom = {};
|
||||
};
|
||||
|
||||
EnvProxy.prototype.set = function(config) {
|
||||
this.config = config;
|
||||
|
||||
// Override environment defaults if proxy config options are set
|
||||
// This will make requests.js follow the proxies in config
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'noProxy')) {
|
||||
this.restoreFrom.NO_PROXY = process.env.NO_PROXY;
|
||||
this.restoreFrom.no_proxy = process.env.no_proxy;
|
||||
delete process.env.no_proxy;
|
||||
process.env.NO_PROXY = config.noProxy;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'proxy')) {
|
||||
this.restoreFrom.HTTP_PROXY = process.env.HTTP_PROXY;
|
||||
this.restoreFrom.http_proxy = process.env.http_proxy;
|
||||
delete process.env.http_proxy;
|
||||
process.env.HTTP_PROXY = config.proxy;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'httpsProxy')) {
|
||||
this.restoreFrom.HTTPS_PROXY = process.env.HTTPS_PROXY;
|
||||
this.restoreFrom.https_proxy = process.env.https_proxy;
|
||||
delete process.env.https_proxy;
|
||||
process.env.HTTPS_PROXY = config.httpsProxy;
|
||||
}
|
||||
};
|
||||
|
||||
EnvProxy.prototype.restore = function() {
|
||||
if (Object.prototype.hasOwnProperty.call(this.config, 'noProxy')) {
|
||||
if (this.restoreFrom.NO_PROXY !== undefined) {
|
||||
process.env.NO_PROXY = this.restoreFrom.NO_PROXY;
|
||||
} else {
|
||||
delete process.env.NO_PROXY;
|
||||
}
|
||||
|
||||
if (this.restoreFrom.no_proxy !== undefined) {
|
||||
process.env.no_proxy = this.restoreFrom.no_proxy;
|
||||
} else {
|
||||
delete process.env.no_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.config, 'proxy')) {
|
||||
if (this.restoreFrom.HTTP_PROXY !== undefined) {
|
||||
process.env.HTTP_PROXY = this.restoreFrom.HTTP_PROXY;
|
||||
} else {
|
||||
delete process.env.HTTP_PROXY;
|
||||
}
|
||||
|
||||
if (this.restoreFrom.http_proxy !== undefined) {
|
||||
process.env.http_proxy = this.restoreFrom.http_proxy;
|
||||
} else {
|
||||
delete process.env.http_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.config, 'httpsProxy')) {
|
||||
if (this.restoreFrom.HTTPS_PROXY !== undefined) {
|
||||
process.env.HTTPS_PROXY = this.restoreFrom.HTTPS_PROXY;
|
||||
} else {
|
||||
delete process.env.HTTPS_PROXY;
|
||||
}
|
||||
|
||||
if (this.restoreFrom.https_proxy !== undefined) {
|
||||
process.env.https_proxy = this.restoreFrom.https_proxy;
|
||||
} else {
|
||||
delete process.env.https_proxy;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EnvProxy;
|
||||
157
packages/bower-config/lib/util/rc.js
Normal file
157
packages/bower-config/lib/util/rc.js
Normal file
@@ -0,0 +1,157 @@
|
||||
var path = require('path');
|
||||
var fs = require('graceful-fs');
|
||||
var optimist = require('optimist');
|
||||
var osenv = require('osenv');
|
||||
var object = require('mout/object');
|
||||
var string = require('mout/string');
|
||||
var paths = require('./paths');
|
||||
var defaults = require('./defaults');
|
||||
|
||||
var win = process.platform === 'win32';
|
||||
var home = osenv.home();
|
||||
|
||||
function rc(name, cwd, argv) {
|
||||
var argvConfig;
|
||||
|
||||
argv = argv || optimist.argv;
|
||||
|
||||
// Parse --config.foo=false
|
||||
argvConfig = object.map(argv.config || {}, function(value) {
|
||||
return value === 'false' ? false : value;
|
||||
});
|
||||
|
||||
// If we have specified a cwd then use this as the base for getting config.
|
||||
cwd = argvConfig.cwd ? argvConfig.cwd : cwd;
|
||||
|
||||
if (cwd) {
|
||||
return object.deepMixIn.apply(null, [
|
||||
{},
|
||||
defaults,
|
||||
{ cwd: cwd },
|
||||
win ? {} : json(path.join('/etc', name + 'rc')),
|
||||
!home ? {} : json(path.join(home, '.' + name + 'rc')),
|
||||
json(path.join(paths.config, name + 'rc')),
|
||||
json(find('.' + name + 'rc', cwd)),
|
||||
env('npm_package_config_' + name + '_'),
|
||||
env(name + '_'),
|
||||
argvConfig
|
||||
]);
|
||||
} else {
|
||||
return object.deepMixIn.apply(null, [
|
||||
{},
|
||||
defaults,
|
||||
win ? {} : json(path.join('/etc', name + 'rc')),
|
||||
!home ? {} : json(path.join(home, '.' + name + 'rc')),
|
||||
json(path.join(paths.config, name + 'rc')),
|
||||
env('npm_package_config_' + name + '_'),
|
||||
env(name + '_'),
|
||||
argvConfig
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function parse(content, file) {
|
||||
var error;
|
||||
|
||||
if (!content.trim().length) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
if (file) {
|
||||
error = new Error('Unable to parse ' + file + ': ' + e.message);
|
||||
} else {
|
||||
error = new Error('Unable to parse rc config: ' + e.message);
|
||||
}
|
||||
|
||||
error.details = content;
|
||||
error.code = 'EMALFORMED';
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function json(file) {
|
||||
var content = {};
|
||||
if (!Array.isArray(file)) {
|
||||
try {
|
||||
content = fs.readFileSync(file).toString();
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parse(content, file);
|
||||
} else {
|
||||
// This is multiple json files
|
||||
file.forEach(function(filename) {
|
||||
if (fs.statSync(filename).isDirectory()) {
|
||||
var error;
|
||||
error = new Error(filename + ' should not be a directory');
|
||||
error.code = 'EFILEISDIR';
|
||||
throw error;
|
||||
}
|
||||
var json = fs.readFileSync(filename).toString();
|
||||
json = parse(json, filename);
|
||||
content = object.merge(content, json);
|
||||
});
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
function env(prefix) {
|
||||
var obj = {};
|
||||
var prefixLength = prefix.length;
|
||||
|
||||
prefix = prefix.toLowerCase();
|
||||
|
||||
object.forOwn(process.env, function(value, key) {
|
||||
key = key.toLowerCase();
|
||||
|
||||
if (string.startsWith(key, prefix)) {
|
||||
var parsedKey = key
|
||||
.substr(prefixLength)
|
||||
.replace(/__/g, '.') // __ is used for nesting
|
||||
.replace(/_/g, '-'); // _ is used as a - separator
|
||||
|
||||
//use a convention patern to accept array from process.env
|
||||
//e.g. export bower_registry__search='["http://abc.com","http://def.com"]'
|
||||
var match = /\[([^\]]*)\]/g.exec(value);
|
||||
var targetValue;
|
||||
if (!match || match.length === 0) {
|
||||
targetValue = value;
|
||||
} else {
|
||||
targetValue = match[1].split(',').map(function(m) {
|
||||
return m.trim();
|
||||
});
|
||||
}
|
||||
object.set(obj, parsedKey, targetValue);
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function find(filename, dir) {
|
||||
var files = [];
|
||||
|
||||
var walk = function(filename, dir) {
|
||||
var file = path.join(dir, filename);
|
||||
var parent = path.dirname(dir);
|
||||
|
||||
if (fs.existsSync(file)) {
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
if (parent !== dir) {
|
||||
walk(filename, parent);
|
||||
}
|
||||
};
|
||||
|
||||
walk(filename, dir);
|
||||
files.reverse();
|
||||
return files;
|
||||
}
|
||||
|
||||
module.exports = rc;
|
||||
35
packages/bower-config/package.json
Normal file
35
packages/bower-config/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "bower-config",
|
||||
"version": "1.4.0",
|
||||
"description": "The Bower config reader and writer.",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/bower/bower/tree/master/packages/bower-config",
|
||||
"main": "lib/Config",
|
||||
"homepage": "http://bower.io",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.3",
|
||||
"mout": "^1.0.0",
|
||||
"optimist": "^0.6.1",
|
||||
"osenv": "^0.1.3",
|
||||
"untildify": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expect.js": "^0.3.1",
|
||||
"glob": "^4.5.3",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mocha": "^2.5.3",
|
||||
"node-uuid": "^1.4.3",
|
||||
"q": "^1.2.0",
|
||||
"rimraf": "^2.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ca": "Equifax Secure CA\n=================\n-----BEGIN CERTIFICATE-----\nMIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE\nChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5\nMB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT\nB0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB\nnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR\nfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW\n8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG\nA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE\nCxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG\nA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS\nspXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB\nAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961\nzgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB\nBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95\n70+sB3c4\n-----END CERTIFICATE-----\n\nGlobalSign Root CA\n==================\n-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\nGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\nb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\nBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\nVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\nDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\nTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\nKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\nc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\ngzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\nAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\nY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\nj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\nhm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\nX4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n-----END CERTIFICATE-----"
|
||||
}
|
||||
3
packages/bower-config/test/assets/custom-ca/.bowerrc
Normal file
3
packages/bower-config/test/assets/custom-ca/.bowerrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ca": "test/assets/custom-ca/ca-bundle.crt"
|
||||
}
|
||||
40
packages/bower-config/test/assets/custom-ca/ca-bundle.crt
Normal file
40
packages/bower-config/test/assets/custom-ca/ca-bundle.crt
Normal file
@@ -0,0 +1,40 @@
|
||||
Equifax Secure CA
|
||||
=================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
|
||||
ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
|
||||
MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
|
||||
B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
|
||||
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
|
||||
fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
|
||||
8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
|
||||
A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
|
||||
CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
|
||||
A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
|
||||
spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
|
||||
Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
|
||||
zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
|
||||
BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
|
||||
70+sB3c4
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
GlobalSign Root CA
|
||||
==================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
|
||||
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
|
||||
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
|
||||
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
|
||||
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
|
||||
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
|
||||
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
|
||||
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
|
||||
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
|
||||
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
|
||||
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
|
||||
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
|
||||
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
|
||||
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
|
||||
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"scripts" : {
|
||||
"postinstall" : "${_myshellvar}"
|
||||
},
|
||||
"storage" : {
|
||||
"packages" : "${_BOWERRC_MY_PACKAGES}",
|
||||
"registry" : {
|
||||
"register": "~/.bower-test/registry",
|
||||
"search": [
|
||||
"${_BOWERRC_MY_USER}:${_BOWERRC_MY_PASS}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tmp" : "${_BOWERRC_MY_TMP}"
|
||||
}
|
||||
5
packages/bower-config/test/assets/env-variables/.bowerrc
Normal file
5
packages/bower-config/test/assets/env-variables/.bowerrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"proxy": "http://HTTP_PROXY",
|
||||
"https-proxy": "http://HTTPS_PROXY",
|
||||
"no-proxy": "google.com"
|
||||
}
|
||||
131
packages/bower-config/test/helpers.js
Normal file
131
packages/bower-config/test/helpers.js
Normal file
@@ -0,0 +1,131 @@
|
||||
var Q = require('q');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('rimraf');
|
||||
var uuid = require('node-uuid');
|
||||
var object = require('mout/object');
|
||||
var fs = require('fs');
|
||||
var glob = require('glob');
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
|
||||
// For better promise errors
|
||||
Q.longStackSupport = true;
|
||||
|
||||
var tmpLocation = path.join(
|
||||
os.tmpdir ? os.tmpdir() : os.tmpDir(),
|
||||
'bower-config-tests',
|
||||
uuid.v4().slice(0, 8)
|
||||
);
|
||||
|
||||
after(function() {
|
||||
rimraf.sync(tmpLocation);
|
||||
});
|
||||
|
||||
exports.TempDir = (function() {
|
||||
function TempDir(defaults) {
|
||||
this.path = path.join(tmpLocation, uuid.v4());
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
TempDir.prototype.create = function(files, defaults) {
|
||||
var that = this;
|
||||
|
||||
defaults = defaults || this.defaults || {};
|
||||
files = object.merge(files || {}, defaults);
|
||||
|
||||
this.meta = function(tag) {
|
||||
if (tag) {
|
||||
return files[tag]['bower.json'];
|
||||
} else {
|
||||
return files['bower.json'];
|
||||
}
|
||||
};
|
||||
|
||||
if (files) {
|
||||
object.forOwn(files, function(contents, filepath) {
|
||||
if (typeof contents === 'object') {
|
||||
contents = JSON.stringify(contents, null, ' ') + '\n';
|
||||
}
|
||||
|
||||
var fullPath = path.join(that.path, filepath);
|
||||
mkdirp.sync(path.dirname(fullPath));
|
||||
fs.writeFileSync(fullPath, contents);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
TempDir.prototype.prepare = function(files) {
|
||||
rimraf.sync(this.path);
|
||||
mkdirp.sync(this.path);
|
||||
this.create(files);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// TODO: Rewrite to synchronous form
|
||||
TempDir.prototype.prepareGit = function(revisions) {
|
||||
var that = this;
|
||||
|
||||
revisions = object.merge(revisions || {}, this.defaults);
|
||||
|
||||
rimraf.sync(that.path);
|
||||
|
||||
mkdirp.sync(that.path);
|
||||
|
||||
var promise = new Q();
|
||||
|
||||
object.forOwn(revisions, function(files, tag) {
|
||||
promise = promise
|
||||
.then(function() {
|
||||
return that.git('init');
|
||||
})
|
||||
.then(function() {
|
||||
that.glob('./!(.git)').map(function(removePath) {
|
||||
var fullPath = path.join(that.path, removePath);
|
||||
|
||||
rimraf.sync(fullPath);
|
||||
});
|
||||
|
||||
that.create(files, {});
|
||||
})
|
||||
.then(function() {
|
||||
return that.git('add', '-A');
|
||||
})
|
||||
.then(function() {
|
||||
return that.git('commit', '-m"commit"');
|
||||
})
|
||||
.then(function() {
|
||||
return that.git('tag', tag);
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
TempDir.prototype.glob = function(pattern) {
|
||||
return glob.sync(pattern, {
|
||||
cwd: this.path,
|
||||
dot: true
|
||||
});
|
||||
};
|
||||
|
||||
TempDir.prototype.getPath = function(name) {
|
||||
return path.join(this.path, name);
|
||||
};
|
||||
|
||||
TempDir.prototype.read = function(name) {
|
||||
return fs.readFileSync(this.getPath(name), 'utf8');
|
||||
};
|
||||
|
||||
TempDir.prototype.readJson = function(name) {
|
||||
return JSON.parse(this.read(name));
|
||||
};
|
||||
|
||||
TempDir.prototype.exists = function(name) {
|
||||
return fs.existsSync(path.join(this.path, name));
|
||||
};
|
||||
|
||||
return TempDir;
|
||||
})();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user