mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Compare commits
800 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a8a4f9029 | ||
|
|
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 | ||
|
|
9e5cd572f8 | ||
|
|
69cd360551 | ||
|
|
4793fc0d1c | ||
|
|
aaaa9cd530 | ||
|
|
c2e0dc9d23 | ||
|
|
fc4446247c | ||
|
|
f4e0b3dfba | ||
|
|
4d7cdb0556 | ||
|
|
037bbff17e | ||
|
|
f05cd5fb94 | ||
|
|
358a73b98e | ||
|
|
32cbb5a0e8 | ||
|
|
8679ad77ae | ||
|
|
1b1d8bdad6 | ||
|
|
aecdf3f365 | ||
|
|
1cf87041cc | ||
|
|
5116fec1ab | ||
|
|
8f2668a24d | ||
|
|
e7516e4bcb | ||
|
|
3853d1297e | ||
|
|
1e398f999b | ||
|
|
3a0ea968f4 | ||
|
|
7de2e5d601 | ||
|
|
b1c45bb586 | ||
|
|
f4cb047e9d | ||
|
|
f494ae7ddd | ||
|
|
44e71267c1 | ||
|
|
52aa684949 | ||
|
|
46655b7c4e | ||
|
|
16cde3118a | ||
|
|
7c2dfc1146 | ||
|
|
53eeca97d3 | ||
|
|
9201a379d6 | ||
|
|
2052ba3eed | ||
|
|
b32bd8b877 | ||
|
|
233f685c61 | ||
|
|
5fe8df2e0a | ||
|
|
a3ae3b66b7 | ||
|
|
2000d2f5db | ||
|
|
84d8d8c57f | ||
|
|
f9ea3846e2 | ||
|
|
8b6c20239e | ||
|
|
73171766e7 | ||
|
|
88b3655829 | ||
|
|
3241e8ed62 | ||
|
|
0a81308e98 | ||
|
|
8d76b87d65 | ||
|
|
f2884656c0 | ||
|
|
6fff6fa707 | ||
|
|
1357f63a1b | ||
|
|
529d702959 | ||
|
|
0155a70457 | ||
|
|
a6ca2ae9bb | ||
|
|
78e443db0a | ||
|
|
5f24eab32d | ||
|
|
f4620b28ab | ||
|
|
9f4c2384ea | ||
|
|
e85a5f778f | ||
|
|
5283a132bc | ||
|
|
db1453f7c0 | ||
|
|
878a228a7d | ||
|
|
b33041c3ec | ||
|
|
c8a6ff38a0 | ||
|
|
573b84f7f4 | ||
|
|
ef67955c21 | ||
|
|
36a14b9b37 | ||
|
|
96d986f436 | ||
|
|
394dd7c8d2 | ||
|
|
6c67d07cc8 | ||
|
|
cd7bbab310 | ||
|
|
4b4a854ed8 | ||
|
|
8194bcb4c6 | ||
|
|
e5d478a1cc | ||
|
|
bbaaee67a1 | ||
|
|
ad27112b58 | ||
|
|
38c3cee1a7 | ||
|
|
b485c5d3cb | ||
|
|
d63047b4ee | ||
|
|
f0a54d0018 | ||
|
|
1d73764788 | ||
|
|
9d2681b0c4 | ||
|
|
f3330e8612 | ||
|
|
11996c04b7 | ||
|
|
35e73a619a | ||
|
|
fe615fd517 | ||
|
|
3e3b64218d | ||
|
|
afe76e57f8 | ||
|
|
6ee3ef7aa8 | ||
|
|
64db869bd4 | ||
|
|
a4ea05800d | ||
|
|
8cf897cd19 | ||
|
|
d06af7a3d7 | ||
|
|
d4fd71986e | ||
|
|
3154444556 | ||
|
|
24f8b913b9 | ||
|
|
fe6b6863ea | ||
|
|
671c23ad50 | ||
|
|
5384fa54b1 | ||
|
|
4bfa8227d9 | ||
|
|
55d78f7928 | ||
|
|
2110148830 | ||
|
|
afc4bfbd42 | ||
|
|
9c42a008aa | ||
|
|
67884744c3 | ||
|
|
ed881e3f29 | ||
|
|
e6e60d5d5e | ||
|
|
d8f166a933 | ||
|
|
bb626d1605 | ||
|
|
daa5b8ddf9 | ||
|
|
db087dfe13 | ||
|
|
848e401efd | ||
|
|
c17c725057 | ||
|
|
2b31f6c07a | ||
|
|
3cf597fccf | ||
|
|
e2adbc37f1 | ||
|
|
6c3b7dbf58 | ||
|
|
d3ab3c1fa7 | ||
|
|
b1ba9be7f6 | ||
|
|
1e5122c023 | ||
|
|
4255d7d4a8 | ||
|
|
cdf45239f4 | ||
|
|
8b2fad32f6 | ||
|
|
d1ae0b1982 | ||
|
|
87cf578ba8 | ||
|
|
3ead440c7c | ||
|
|
e168c894a2 | ||
|
|
75e3661371 | ||
|
|
baf8f7bf6b | ||
|
|
88758cd98c | ||
|
|
3bd2d62e67 | ||
|
|
d3eef5772a | ||
|
|
7c714901d4 | ||
|
|
7792b6d35d | ||
|
|
2db983dba3 | ||
|
|
b9718bb309 | ||
|
|
26f609e614 | ||
|
|
4c2b56096b | ||
|
|
5af929f0be | ||
|
|
6a18cde782 | ||
|
|
32c5538fc5 | ||
|
|
57478d86c7 | ||
|
|
42107e6fea | ||
|
|
686e883d87 | ||
|
|
f2767648e7 | ||
|
|
9e4bdd270d | ||
|
|
7cb88ab49f | ||
|
|
a532c55dca | ||
|
|
c559432c19 | ||
|
|
9aae3b8d7e | ||
|
|
322c49edf4 | ||
|
|
34ec39507c | ||
|
|
fe9b27a647 | ||
|
|
304bb36bbc | ||
|
|
376a2de600 | ||
|
|
1605374a25 | ||
|
|
ac244a1400 | ||
|
|
9e58bbca31 | ||
|
|
027a0694f7 | ||
|
|
d50e50f3b5 | ||
|
|
3030469c59 | ||
|
|
977e0ddc52 | ||
|
|
de3e1089da | ||
|
|
7897ad7dba | ||
|
|
accdab3ece | ||
|
|
612aaa88eb | ||
|
|
338ac99080 | ||
|
|
9605bbea5f | ||
|
|
7bc97a1241 | ||
|
|
bd2c253f99 | ||
|
|
f1efce7d1f | ||
|
|
844cc5a527 | ||
|
|
0e27b6f813 | ||
|
|
3791aee9d6 | ||
|
|
20a223a959 | ||
|
|
9219f54718 | ||
|
|
ea5bd51327 | ||
|
|
5a2272cab1 | ||
|
|
b94c20b8da | ||
|
|
b81ba140e3 | ||
|
|
4ffdb500b9 | ||
|
|
609607b096 | ||
|
|
e9657668a9 | ||
|
|
1696cde273 | ||
|
|
94ffc35b25 | ||
|
|
5a1e5eb9c7 | ||
|
|
8c1f30b1c8 | ||
|
|
e3f402fc66 | ||
|
|
6616d09f47 | ||
|
|
25ad2ef946 | ||
|
|
ba4a1a9d45 | ||
|
|
3a37202dc5 | ||
|
|
12258324d3 | ||
|
|
2adb0b0807 | ||
|
|
19fc84007d | ||
|
|
8fcbd3671d | ||
|
|
0eadbef02d | ||
|
|
107ad1d3fc | ||
|
|
d2ba80e6e9 | ||
|
|
f18b38cde5 | ||
|
|
e6f1805df0 | ||
|
|
4da1b62542 | ||
|
|
852a586d5c | ||
|
|
e8a2d92785 | ||
|
|
50ed13e4ee | ||
|
|
cb9b737b9d | ||
|
|
700b46162c | ||
|
|
0e1153f610 | ||
|
|
8669ed2aac | ||
|
|
6d12ef291b | ||
|
|
ca0a36abcf | ||
|
|
4c6fdc905f | ||
|
|
cdbc4a123c | ||
|
|
bb7c02b07b | ||
|
|
4f42aeabd7 | ||
|
|
b77517ef64 | ||
|
|
b9c3f750eb | ||
|
|
aaecbfab17 | ||
|
|
c4539aa603 | ||
|
|
7f801319bf | ||
|
|
1a990f4563 | ||
|
|
944a328f30 | ||
|
|
e11b60d812 | ||
|
|
c91e99b782 | ||
|
|
da8ec1e4ab | ||
|
|
c8d5199815 | ||
|
|
e7868f0fb1 | ||
|
|
eca46dbd85 | ||
|
|
67bd5d026f | ||
|
|
51de67cc73 | ||
|
|
3d4f9cd919 | ||
|
|
cd8d397e63 | ||
|
|
5a9c099188 | ||
|
|
2137089a70 | ||
|
|
fbd02852a3 | ||
|
|
f2584ade24 | ||
|
|
2f72cd4b7d | ||
|
|
50bfd14968 | ||
|
|
2ff53fc448 | ||
|
|
42cd2e584f | ||
|
|
49de3cca62 | ||
|
|
718db0309f | ||
|
|
d867095f50 | ||
|
|
e51bf20e72 | ||
|
|
89286e628b | ||
|
|
c8042b4781 | ||
|
|
15f8a30cd4 | ||
|
|
9fdd96c92b | ||
|
|
ce15df27ca | ||
|
|
468657bfe2 | ||
|
|
814180d129 | ||
|
|
93be2fef6d | ||
|
|
3ab71f27ff | ||
|
|
38fa1b6858 | ||
|
|
c6ed215260 | ||
|
|
74eba8d2e8 | ||
|
|
5a72dae2c8 | ||
|
|
6194821643 | ||
|
|
37aab9c72e | ||
|
|
0a0dc8cef9 | ||
|
|
c6d89b79b4 | ||
|
|
8a435dff27 | ||
|
|
459925eba7 | ||
|
|
6614a43658 | ||
|
|
88fd65ae34 | ||
|
|
83da088024 | ||
|
|
51a986d0d4 | ||
|
|
0aefe8fc0e | ||
|
|
2845984169 | ||
|
|
2a91dc5fb1 | ||
|
|
5eca9274ee | ||
|
|
d57d81ca85 | ||
|
|
a5dcf9cc24 | ||
|
|
484c8985ed | ||
|
|
931b0a8905 | ||
|
|
bfd1e93325 | ||
|
|
bf23751549 | ||
|
|
b79034fbb9 | ||
|
|
eaa05ac6c1 | ||
|
|
8f0a3d727e | ||
|
|
b6a524e6b4 | ||
|
|
43d00deb88 | ||
|
|
4836a0cae9 | ||
|
|
302c4ade51 | ||
|
|
75d80e014a | ||
|
|
0f790f4293 | ||
|
|
452217e9fa | ||
|
|
f66c0cfe5c | ||
|
|
30898c13d3 | ||
|
|
2330d59ffa | ||
|
|
1316be57dc | ||
|
|
b7c19695e7 | ||
|
|
b85cf2683c | ||
|
|
ff0f2a8f83 | ||
|
|
7e5184d342 | ||
|
|
8fa1fd55e9 | ||
|
|
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 |
46
.appveyor.yml
Normal file
46
.appveyor.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
# http://www.appveyor.com/docs/appveyor-yml
|
||||
|
||||
# Set build version format here instead of in the admin panel.
|
||||
version: "{build}"
|
||||
|
||||
# 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: "4"
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "8"
|
||||
- nodejs_version: "9"
|
||||
|
||||
# Finish on first failed build
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Install node, display versions, install dependencies
|
||||
install:
|
||||
- ps: Install-Product node 8
|
||||
- node --version && npm --version
|
||||
- git --version && svn --version
|
||||
- npm install -g yarn grunt
|
||||
- yarn
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
- cmd: npm run ci
|
||||
|
||||
# Make clone much faster
|
||||
shallow_clone: true
|
||||
|
||||
# Disable Visual Studio build and deploy
|
||||
build: off
|
||||
deploy: off
|
||||
|
||||
# Cache node modules, and refresh if package.json changes
|
||||
cache:
|
||||
- "%LOCALAPPDATA%\\Yarn"
|
||||
@@ -15,5 +15,4 @@ trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
52
.eslintrc
Normal file
52
.eslintrc
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"no-bitwise": 0,
|
||||
"curly": 0,
|
||||
"eqeqeq": 0,
|
||||
"guard-for-in": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-caller": 2,
|
||||
"no-new": 2,
|
||||
"no-plusplus": 0,
|
||||
"no-undef": 2,
|
||||
"no-unused-vars": 0,
|
||||
"strict": 0,
|
||||
"semi": 0,
|
||||
"comma-spacing": 2,
|
||||
"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,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-unreachable": 2,
|
||||
"valid-typeof": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-eq-null": 0,
|
||||
"no-eval": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"block-scoped-var": 0,
|
||||
"no-iterator": 0,
|
||||
"no-loop-func": 2,
|
||||
"no-script-url": 0,
|
||||
"no-shadow": 0,
|
||||
"no-new-func": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-invalid-this": 0,
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"keyword-spacing": 2,
|
||||
"new-parens": 2,
|
||||
"no-multiple-empty-lines": [2, { max: 2}],
|
||||
"eol-last": 2,
|
||||
"no-trailing-spaces": 2
|
||||
}
|
||||
}
|
||||
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:**
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
!lib/bin
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
|
||||
@@ -9,3 +10,6 @@
|
||||
/bower.json
|
||||
/component.json
|
||||
/bower_components
|
||||
/test/sample
|
||||
!/test/sample/bower.json
|
||||
/npm-shrinkwrap.json
|
||||
|
||||
2
.prettierignore
Normal file
2
.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
||||
**/node_modules/**
|
||||
**/test/assets/**
|
||||
38
.travis.yml
38
.travis.yml
@@ -1,27 +1,43 @@
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
|
||||
# Use node 8 for build
|
||||
node_js:
|
||||
- "8"
|
||||
|
||||
# Then test with specific node version
|
||||
env:
|
||||
- NODE_VERSION=0.10
|
||||
- NODE_VERSION=0.11
|
||||
- TEST_NODE_VERSION="0.10"
|
||||
- TEST_NODE_VERSION="0.12"
|
||||
- TEST_NODE_VERSION="4"
|
||||
- TEST_NODE_VERSION="6"
|
||||
- TEST_NODE_VERSION="8"
|
||||
- TEST_NODE_VERSION="9"
|
||||
|
||||
before_install:
|
||||
- node --version
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.5.1
|
||||
- export PATH=$HOME/.yarn/bin:$PATH
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
|
||||
install:
|
||||
- test $TRAVIS_OS_NAME = "osx" && brew install nvm && source $(brew --prefix nvm)/nvm.sh || test $TRAVIS_OS_NAME = "linux"
|
||||
- nvm install $NODE_VERSION
|
||||
- node --version
|
||||
- npm --version
|
||||
- git --version
|
||||
- svn --version | head -n 1
|
||||
- npm install -g grunt-cli
|
||||
- npm install
|
||||
- yarn
|
||||
- nvm install $TEST_NODE_VERSION
|
||||
- npm install -g grunt
|
||||
|
||||
os:
|
||||
- osx
|
||||
- linux
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- os: osx
|
||||
- env: "NODE_VERSION=0.11"
|
||||
|
||||
script:
|
||||
- nvm use $TEST_NODE_VERSION
|
||||
- node --version && npm --version && git --version && svn --version | head -n 1
|
||||
- grunt travis
|
||||
|
||||
160
CHANGELOG.md
160
CHANGELOG.md
@@ -1,5 +1,156 @@
|
||||
# Changelog
|
||||
|
||||
## 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
|
||||
- Update graceful-fs to 4.x in all dependences, fixes nodejs/node#5213
|
||||
- Resolve pluggable resolvers using cwd and fallback to global modules, fixes #1919
|
||||
- Upgrade handlebars to 4.0.5, closes #2195
|
||||
- Replace all % chatacters in defined scripts, instead of only first one, fixes #2174
|
||||
- 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
|
||||
|
||||
Revert locations of all files while still packaging `node_modules`.
|
||||
|
||||
It's because people are depending on internals of bower, like
|
||||
`bower/lib/renderers/StandardRenderer`. We want to preserve this
|
||||
implicit contract, but we discourage it. The only official way
|
||||
to use bower programmatically is through `require('bower')`.
|
||||
|
||||
## 1.7.6 - 2016-01-27
|
||||
|
||||
- Revert location of "bin/bower" as developers are using it directly ([#2157](https://github.com/bower/bower/issues/2157))
|
||||
Note: Correctly, you should use an alias created in `npm bin --global`.
|
||||
|
||||
## 1.7.5 - 2016-01-26
|
||||
|
||||
- Remove analytics from Bower, fixes ([#2150](https://github.com/bower/bower/pull/2150))
|
||||
- Default to ^ operator on `bower install --save` ([#2145](https://github.com/bower/bower/pull/2145))
|
||||
- Support absolute path in .bowerrc directory option ([#2130](https://github.com/bower/bower/pull/2130))
|
||||
- Display user's name upon `bower login` command ([#2133](https://github.com/bower/bower/pull/2133))
|
||||
- Decompress gzip files ([#2092](https://github.com/bower/bower/pull/2092))
|
||||
- Prevent name clashes in package extraction ([#2102](https://github.com/bower/bower/pull/2102))
|
||||
- When strictSsl is false, set GIT_SSL_NO_VERIFY=true ([#2129](https://github.com/bower/bower/issues/2129))
|
||||
- Distribute bower with npm@3 for better Windows support ([#2146](https://github.com/bower/bower/issues/2146))
|
||||
- Update request to 2.67.0 and fs-write-stream-atomic to 1.0.8
|
||||
- Documentation improvements
|
||||
|
||||
## 1.7.4 - 2016-01-21
|
||||
|
||||
Unpublished because of issue with npm distribution:
|
||||
https://github.com/npm/npm/issues/11227
|
||||
|
||||
## 1.7.3 - 2016-01-20
|
||||
|
||||
Unpublished because of issue with npm distribution:
|
||||
https://github.com/npm/npm/issues/11227
|
||||
|
||||
## 1.7.2 - 2015-12-31
|
||||
|
||||
- Lock "fs-write-stream-atomic" to 1.0.5
|
||||
|
||||
## 1.7.1 - 2015-12-11
|
||||
|
||||
- Rollback "Add `bower update --save` functionality", it causes issues and needs more testing
|
||||
- Fix backward-compatibility of `bower search --json` ([#2066](https://github.com/bower/bower/issues/2066))
|
||||
- Ignore prerelease versions from `bower info` output
|
||||
- Update update-notifier to 0.6.0
|
||||
- Better formatting of help messages (https://github.com/bower/bower/commit/de3e1089da80f47ea3667c5ab80d301cddfd8c3e)
|
||||
- Add help menu for update `--save` and `update --save-dev` (https://github.com/bower/bower/commit/612aaa88eb4d4b268b2d8665c338ac086af3a5b0)
|
||||
|
||||
## 1.7.0 - 2015-12-07
|
||||
|
||||
- Add `bower update --save` functionality ([#2035](https://github.com/bower/bower/issues/2035))
|
||||
- `bower search` shows help message when no package name is specified ([#2066](https://github.com/bower/bower/issues/2066))
|
||||
- Update only those packages that are explicitly requested by the user. Related Issues
|
||||
- [#256](https://github.com/bower/bower/issues/256)
|
||||
- [#924](https://github.com/bower/bower/issues/924)
|
||||
- [#1770](https://github.com/bower/bower/issues/1770)
|
||||
- Allow for @ in username for SVN on windows ([#1650](https://github.com/bower/bower/issues/1650))
|
||||
- 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))
|
||||
|
||||
## 1.6.9 - 2015-12-04
|
||||
|
||||
- Change git version of fs-write-stream-atomic back to npm version ([#2079](https://github.com/bower/bower/issues/2079))
|
||||
|
||||
## 1.6.8 - 2015-11-27
|
||||
|
||||
- Use fs-write-stream-atomic for downloads
|
||||
- Improved downloader that properly cleans after itself
|
||||
- Fix shallow host detection ([#2040](https://github.com/bower/bower/pull/2040))
|
||||
- Upgrade to ([bower-config#1.2.3](https://github.com/bower/config/releases/tag/1.2.3))
|
||||
- Properly restore env variables if they are undefined at the beginning
|
||||
- Properly handle `default` setting for config.ca
|
||||
- Display proper error if .bowerrc is a directory instead of file
|
||||
|
||||
## 1.6.7 - 2015-11-26
|
||||
|
||||
- Bundless all the dependencies again
|
||||
|
||||
## 1.6.6 - 2015-11-25
|
||||
|
||||
- Fixes regression with the published npm version
|
||||
|
||||
## 1.6.5 - 2015-10-24
|
||||
|
||||
- Updates to tests and documentation
|
||||
- Fixes passing options when requesting downloads
|
||||
|
||||
## 1.6.4 - 2015-10-24
|
||||
|
||||
- Fix ignoring dependencies on multiple install run ([#1970](https://github.com/bower/bower/pull/1970))
|
||||
- Use --non-interactive when running svn client ([#1969](https://github.com/bower/bower/pull/1969))
|
||||
- Fix downloading of URLs ending with slash ([#1956](https://github.com/bower/bower/pull/1956))
|
||||
- Add user-agent field for downloads by Bower ([#1960](https://github.com/bower/bower/pull/1960))
|
||||
|
||||
## 1.6.3 - 2015-10-16
|
||||
|
||||
Fixes regression issues introduced with 1.6.2, specifically:
|
||||
|
||||
- Allow for bower_components to be a symlink
|
||||
- Allow setting custom registry in .bowerrc
|
||||
|
||||
## 1.6.2 - 2015-10-15
|
||||
|
||||
Fix dependency issues of 1.6.1. First published release of 1.6.x.
|
||||
|
||||
## 1.6.1 - 2015-10-15
|
||||
|
||||
Fix dependency issues of 1.6.0. Reverted release.
|
||||
|
||||
## 1.6.0 - 2015-10-15
|
||||
|
||||
- Shrinkwrap all dependencies and add them to bundledDependencies ([#1948](https://github.com/bower/bower/pull/1948))
|
||||
@@ -12,6 +163,11 @@
|
||||
- Close file-handles when possible. Prevents all sorts of permission issues on Windows ([0bb1536](https://github.com/bower/bower/commit/0bb1536c9972e13f3be06bea9a8619632966c664))
|
||||
- Prevent ENOENT error on Windows when in VM environment ([isaacs/chmodr#8](https://github.com/isaacs/chmodr/pull/8))
|
||||
|
||||
Reverted release.
|
||||
|
||||
## 1.5.4 - 2015-11-24
|
||||
|
||||
- [fix] Lock lru-cache dependency to 2.7.0
|
||||
|
||||
## 1.5.3 - 2015-09-24
|
||||
|
||||
@@ -38,6 +194,10 @@
|
||||
- Make bower commands work from subdirectories ([#1866](https://github.com/bower/bower/issues/1866))
|
||||
- No longer prefer installing bower as global module ([#1865](https://github.com/bower/bower/issues/1865))
|
||||
|
||||
## 1.4.2 - 2015-11-24
|
||||
|
||||
- [fix] Lock lru-cache dependency to 2.7.0
|
||||
|
||||
## 1.4.1 - 2015-04-01
|
||||
|
||||
- [fix] Reading .bowerrc upwards directory tree ([#1763](https://github.com/bower/bower/issues/1763))
|
||||
|
||||
@@ -1,35 +1,26 @@
|
||||
# Contributing to Bower
|
||||
|
||||
Bower is a large community project with many different developers contributing at all levels to the project. We're **actively** looking for more contributors right now. (Jan 2014)
|
||||
Bower is a large community project with many different developers contributing at all levels to the project. We're **actively** looking for more contributors right now. If you're interested in becoming a Bower maintainer or supporting in any way, please fill the following form: http://goo.gl/forms/P1ndzCNoiG. There is more information about [contributing](https://github.com/bower/bower/wiki/Contributor-Guidelines) in the Wiki.
|
||||
|
||||
<a name="bugs"></a>
|
||||
## 🐛 [Bug reports](https://github.com/bower/bower/wiki/Report-a-Bug)
|
||||
|
||||
## Casual Involvement
|
||||
|
||||
* Improve the bower.io site ([tickets](https://github.com/bower/bower.github.io/issues))
|
||||
* Move forward [bower.io redesign](https://github.com/bower/bower.github.io/issues/7)
|
||||
* Attend team meetings
|
||||
* Comment on issues and drive to resolution
|
||||
|
||||
## High-impact Involvement
|
||||
|
||||
* Maintaining the bower client.
|
||||
* [Authoring client tests](https://github.com/bower/bower/issues/801)
|
||||
* Maintaining the bower client.
|
||||
* Read [Architecture doc](https://github.com/bower/bower/wiki/Rewrite-architecture)
|
||||
* Triage, close, fix and resolve [issues](https://github.com/bower/bower/issues)
|
||||
* Developing the [new registry server](https://github.com/bower/registry/tree/node_rewrite)
|
||||
* Hooking in to Elastic Search rather than the in-memory search
|
||||
* Getting bower/registry-client to talk to the new server without breaking backwards compatibility
|
||||
* DevOps for the server
|
||||
|
||||
## Team Meetings
|
||||
|
||||
We meet on Monday at 1:00pm PST, 9:00pm UTC in #bower on Freenode. [The meeting notes](http://goo.gl/NJZ1o2).
|
||||
|
||||
<hr>
|
||||
|
||||
Following these guidelines helps to communicate that you respect the time of
|
||||
the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue, assessing
|
||||
changes, and helping you finalize your pull requests.
|
||||
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.
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
@@ -38,57 +29,15 @@ 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), our
|
||||
[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.
|
||||
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
|
||||
<a name="bugs"></a>
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
Good bug reports are extremely helpful - thank you!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
1. **Use the GitHub issue search** — check if the issue has already been
|
||||
reported.
|
||||
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
|
||||
3. **Isolate the problem** — ideally create a [reduced test
|
||||
case](http://css-tricks.com/6263-reduced-test-cases/).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report. What is
|
||||
your environment? What steps will reproduce the issue? What OS experiences the
|
||||
problem? What would you expect to be the outcome? All these details will help
|
||||
people to fix any potential bugs.
|
||||
|
||||
Example:
|
||||
|
||||
> Short and descriptive example bug report title
|
||||
>
|
||||
> A summary of the issue and the browser/OS environment in which it occurs. If
|
||||
> suitable, include the steps required to reproduce the bug.
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> `<url>` - a link to the reduced test case
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
|
||||
<a name="features"></a>
|
||||
## Feature requests
|
||||
|
||||
@@ -172,6 +121,8 @@ included in the project:
|
||||
force push to your remote feature branch. You may also be asked to squash
|
||||
commits.
|
||||
|
||||
10. If you are asked to squash your commits, then please use `git rebase -i master`. It will ask you to pick your commits - pick the major commits and squash the rest.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree to license your work under the
|
||||
same license as that used by the project.
|
||||
|
||||
|
||||
249
Gruntfile.js
249
Gruntfile.js
@@ -1,12 +1,16 @@
|
||||
'use strict';
|
||||
module.exports = function (grunt) {
|
||||
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({
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
eslint: {
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'bin/*',
|
||||
@@ -14,6 +18,7 @@ module.exports = function (grunt) {
|
||||
'test/**/*.js',
|
||||
'!test/assets/**/*',
|
||||
'!test/reports/**/*',
|
||||
'!test/sample/**/*',
|
||||
'!test/tmp/**/*'
|
||||
]
|
||||
},
|
||||
@@ -37,24 +42,242 @@ module.exports = function (grunt) {
|
||||
command: 'node test/packages.js && node test/packages-svn.js'
|
||||
},
|
||||
'assets-force': {
|
||||
command: 'node test/packages.js --force && node test/packages-svn.js --force'
|
||||
command:
|
||||
'node test/packages.js --force && node test/packages-svn.js --force'
|
||||
},
|
||||
cover: {
|
||||
command: 'STRICT_REQUIRE=1 node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- --timeout 30000 -R dot test/test.js'
|
||||
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: 'node node_modules/.bin/coveralls < test/reports/lcov.info'
|
||||
command: 'npm run coveralls < test/reports/lcov.info',
|
||||
exitCodes: [0, 1, 2, 3] // Alow for failure for coverage report
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['<%= jshint.files %>'],
|
||||
tasks: ['jshint', 'simplemocha:short']
|
||||
files: ['<%= eslint.files %>'],
|
||||
tasks: ['eslint', 'simplemocha:short']
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('assets', ['exec:assets-force']);
|
||||
grunt.registerTask('test', ['jshint', 'exec:assets', 'simplemocha:full']);
|
||||
grunt.registerTask('test', ['eslint', 'exec:assets', 'simplemocha:full']);
|
||||
grunt.registerTask('cover', 'exec:cover');
|
||||
grunt.registerTask('travis', ['jshint', 'exec:assets', 'exec:cover', 'exec:coveralls']);
|
||||
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();
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
21
HOOKS.md
21
HOOKS.md
@@ -1,21 +0,0 @@
|
||||
# Install and Uninstall Hooks
|
||||
|
||||
Bower provides 3 separate hooks that can be used to trigger other automated tools during Bower usage. Importantly, these hooks are intended to allow external tools to help wire up the newly installed components into the parent project and other similar tasks. These hooks are not intended to provide a post-installation build step for component authors. As such, the configuration for these hooks is provided in the `.bowerrc` file in the parent project's directory.
|
||||
|
||||
## Configuring
|
||||
|
||||
In `.bowerrc` do:
|
||||
|
||||
```js
|
||||
{
|
||||
"scripts": {
|
||||
"preinstall": "<your command here>",
|
||||
"postinstall": "<your command here>",
|
||||
"preuninstall": "<your command here>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The value of each script hook may contain a % character. When your script is called, the % will be replaced with a space-separated list of components being installed or uninstalled.
|
||||
|
||||
Your script will also include an environment variable `BOWER_PID` containing the PID of the parent Bower process that triggered the script. This can be used to verify that a `preinstall` and `postinstall` steps are part of the same Bower process.
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2015 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
|
||||
|
||||
112
README.md
112
README.md
@@ -1,10 +1,18 @@
|
||||
# Bower
|
||||
# Bower - A package manager for the web
|
||||
|
||||
[](https://travis-ci.org/bower/bower) [](https://ci.appveyor.com/project/sheerun/bower/history) [](https://coveralls.io/r/bower/bower?branch=master)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
|
||||
> ..psst! While Bower is maintained, we recommend [yarn](https://yarnpkg.com/) and [webpack](https://webpack.js.org/) for new front-end projects!
|
||||
|
||||
[](https://travis-ci.org/bower/bower)
|
||||
[](https://ci.appveyor.com/project/bower/bower)
|
||||
[](https://coveralls.io/r/bower/bower?branch=master)
|
||||
[](https://discord.gg/0fFM7QF0KpZRh2cY)
|
||||
|
||||
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
|
||||
|
||||
> A package manager for the web
|
||||
---
|
||||
|
||||
Bower offers a generic, unopinionated solution to the problem of **front-end package management**, while exposing the package dependency model via an API that can be consumed by a more opinionated build stack. There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
|
||||
|
||||
@@ -45,7 +53,7 @@ $ bower install <package>#<version> --save
|
||||
|
||||
We discourage using bower components statically for performance and security reasons (if component has an `upload.php` file that is not ignored, that can be easily exploited to do malicious stuff).
|
||||
|
||||
The best approach is to process components installed by bower with build tool (like [Grunt](http://gruntjs.com/) or [gulp](http://gulpjs.com/)), and serve them concatenated or using module loader (like [RequireJS](http://requirejs.org/)).
|
||||
The best approach is to process components installed by bower with build tool (like [Grunt](http://gruntjs.com/) or [gulp](http://gulpjs.com/)), and serve them concatenated or using a module loader (like [RequireJS](http://requirejs.org/)).
|
||||
|
||||
### Uninstalling packages
|
||||
|
||||
@@ -59,18 +67,19 @@ $ bower uninstall <package-name>
|
||||
|
||||
On `prezto` or `oh-my-zsh`, do not forget to `alias bower='noglob bower'` or `bower install jquery\#1.9.1`
|
||||
|
||||
### Running commands with sudo
|
||||
### Never run Bower with sudo
|
||||
|
||||
Bower is a user command, there is no need to execute it with superuser permissions.
|
||||
However, if you still want to run commands with sudo, use `--allow-root` option.
|
||||
Bower is a user command; there is no need to execute it with superuser permissions.
|
||||
|
||||
### Windows users
|
||||
|
||||
To use Bower on Windows, you must install
|
||||
[msysgit](http://msysgit.github.io/) correctly. Be sure to check the
|
||||
option shown below:
|
||||
[Git for Windows](http://git-for-windows.github.io/) correctly. Be sure to check the
|
||||
options shown below:
|
||||
|
||||

|
||||
<img src="https://cloud.githubusercontent.com/assets/10702007/10532690/d2e8991a-7386-11e5-9a57-613c7f92e84e.png" width="534" height="418" alt="Git for Windows" />
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/10702007/10532694/dbe8857a-7386-11e5-9bd0-367e97644403.png" width="534" height="418" alt="Git for Windows" />
|
||||
|
||||
Note that if you use TortoiseGit and if Bower keeps asking for your SSH
|
||||
password, you should add the following environment variable: `GIT_SSH -
|
||||
@@ -92,17 +101,15 @@ Bower can be configured using JSON in a `.bowerrc` file. Read over available opt
|
||||
|
||||
## Support
|
||||
|
||||
* [Discord chat](https://discord.gg/0fFM7QF0KpZRh2cY)
|
||||
* [StackOverflow](http://stackoverflow.com/questions/tagged/bower)
|
||||
* [Mailinglist](http://groups.google.com/group/twitter-bower) - twitter-bower@googlegroups.com
|
||||
* [\#bower](http://webchat.freenode.net/?channels=bower) on Freenode
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome [contributions](https://github.com/bower/bower/graphs/contributors) of all kinds from anyone. Please take a moment to
|
||||
review the [guidelines for contributing](CONTRIBUTING.md).
|
||||
We welcome [contributions](https://github.com/bower/bower/graphs/contributors) of all kinds from anyone. Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md).
|
||||
|
||||
* [Bug reports](CONTRIBUTING.md#bugs)
|
||||
* [Bug reports](https://github.com/bower/bower/wiki/Report-a-Bug)
|
||||
* [Feature requests](CONTRIBUTING.md#features)
|
||||
* [Pull requests](CONTRIBUTING.md#pull-requests)
|
||||
|
||||
@@ -113,8 +120,81 @@ 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>
|
||||
|
||||
|
||||
## 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>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2015 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
|
||||
|
||||
40
appveyor.yml
40
appveyor.yml
@@ -1,40 +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.11"
|
||||
|
||||
# Allow failing jobs for bleeding-edge Node.js versions.
|
||||
matrix:
|
||||
allow_failures:
|
||||
- nodejs_version: "0.11"
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node 0.STABLE.latest
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
# Install subversion
|
||||
- choco install svn -y
|
||||
# Install bower
|
||||
- npm install
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
# Output useful info for debugging.
|
||||
- node --version
|
||||
- npm --version
|
||||
- cmd: npm test
|
||||
|
||||
# Don't actually build.
|
||||
build: off
|
||||
|
||||
# Set build version format here instead of in the admin panel.
|
||||
version: "{build}"
|
||||
141
bin/bower
141
bin/bower
@@ -1,142 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
process.bin = process.title = 'bower';
|
||||
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var userHome = require('user-home');
|
||||
var bower = require('../lib');
|
||||
var pkg = require('../package.json');
|
||||
var cli = require('../lib/util/cli');
|
||||
var rootCheck = require('../lib/util/rootCheck');
|
||||
var analytics = require('../lib/util/analytics');
|
||||
|
||||
var options;
|
||||
var renderer;
|
||||
var loglevel;
|
||||
var command;
|
||||
var commandFunc;
|
||||
var logger;
|
||||
var levels = Logger.LEVELS;
|
||||
|
||||
options = cli.readOptions({
|
||||
version: { type: Boolean, shorthand: 'v' },
|
||||
help: { type: Boolean, shorthand: 'h' },
|
||||
'allow-root': { type: Boolean }
|
||||
});
|
||||
|
||||
// Handle print of version
|
||||
if (options.version) {
|
||||
process.stdout.write(pkg.version + '\n');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
// Root check
|
||||
rootCheck(options, bower.config);
|
||||
|
||||
// Set loglevel
|
||||
if (bower.config.silent) {
|
||||
loglevel = levels.error;
|
||||
} else if (bower.config.verbose) {
|
||||
loglevel = -Infinity;
|
||||
Q.longStackSupport = true;
|
||||
} else if (bower.config.quiet) {
|
||||
loglevel = levels.warn;
|
||||
} else {
|
||||
loglevel = levels[bower.config.loglevel] || levels.info;
|
||||
}
|
||||
|
||||
// Get the command to execute
|
||||
while (options.argv.remain.length) {
|
||||
command = options.argv.remain.join(' ');
|
||||
|
||||
// Alias lookup
|
||||
if (bower.abbreviations[command]) {
|
||||
command = bower.abbreviations[command].replace(/\s/g, '.');
|
||||
break;
|
||||
}
|
||||
|
||||
command = command.replace(/\s/g, '.');
|
||||
|
||||
// Direct lookup
|
||||
if (mout.object.has(bower.commands, command)) {
|
||||
break;
|
||||
}
|
||||
|
||||
options.argv.remain.pop();
|
||||
}
|
||||
|
||||
// Ask for Insights on first run.
|
||||
analytics.setup(bower.config).then(function () {
|
||||
// Execute the command
|
||||
commandFunc = command && mout.object.get(bower.commands, command);
|
||||
command = command && command.replace(/\./g, ' ');
|
||||
|
||||
// If no command was specified, show bower help
|
||||
// Do the same if the command is unknown
|
||||
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)
|
||||
} else if (options.help || !commandFunc.line) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
// Call the line method
|
||||
} else {
|
||||
logger = commandFunc.line(process.argv);
|
||||
|
||||
// If the method failed to interpret the process arguments
|
||||
// show the command help
|
||||
if (!logger) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
}
|
||||
}
|
||||
|
||||
// Get the renderer and configure it with the executed command
|
||||
renderer = cli.getRenderer(command, logger.json, bower.config);
|
||||
|
||||
logger
|
||||
.on('end', function (data) {
|
||||
if (!bower.config.silent && !bower.config.quiet) {
|
||||
renderer.end(data);
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
// Warn if HOME is not SET
|
||||
if (!userHome) {
|
||||
logger.warn('no-home', 'HOME environment variable not set. User config will not be loaded.');
|
||||
}
|
||||
|
||||
if (bower.config.interactive) {
|
||||
var updateNotifier = require('update-notifier');
|
||||
|
||||
// Check for newer version of Bower
|
||||
var notifier = updateNotifier({pkg: pkg});
|
||||
|
||||
if (notifier.update && levels.info >= loglevel) {
|
||||
notifier.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
require('../lib/bin/bower');
|
||||
|
||||
150
lib/bin/bower.js
Normal file
150
lib/bin/bower.js
Normal file
@@ -0,0 +1,150 @@
|
||||
process.bin = process.title = 'bower';
|
||||
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var userHome = require('user-home');
|
||||
var bower = require('../');
|
||||
var version = require('../version');
|
||||
var cli = require('../util/cli');
|
||||
var rootCheck = require('../util/rootCheck');
|
||||
|
||||
var options;
|
||||
var renderer;
|
||||
var loglevel;
|
||||
var command;
|
||||
var commandFunc;
|
||||
var logger;
|
||||
var levels = Logger.LEVELS;
|
||||
|
||||
options = cli.readOptions({
|
||||
version: { type: Boolean, shorthand: 'v' },
|
||||
help: { type: Boolean, shorthand: 'h' },
|
||||
'allow-root': { type: Boolean }
|
||||
});
|
||||
|
||||
// Handle print of version
|
||||
if (options.version) {
|
||||
process.stdout.write(version + '\n');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
// Root check
|
||||
rootCheck(options, bower.config);
|
||||
|
||||
// Set loglevel
|
||||
if (bower.config.silent) {
|
||||
loglevel = levels.error;
|
||||
} else if (bower.config.verbose) {
|
||||
loglevel = -Infinity;
|
||||
Q.longStackSupport = true;
|
||||
} else if (bower.config.quiet) {
|
||||
loglevel = levels.warn;
|
||||
} else {
|
||||
loglevel = levels[bower.config.loglevel] || levels.info;
|
||||
}
|
||||
|
||||
// Get the command to execute
|
||||
while (options.argv.remain.length) {
|
||||
command = options.argv.remain.join(' ');
|
||||
|
||||
// Alias lookup
|
||||
if (bower.abbreviations[command]) {
|
||||
command = bower.abbreviations[command].replace(/\s/g, '.');
|
||||
break;
|
||||
}
|
||||
|
||||
command = command.replace(/\s/g, '.');
|
||||
|
||||
// Direct lookup
|
||||
if (mout.object.has(bower.commands, command)) {
|
||||
break;
|
||||
}
|
||||
|
||||
options.argv.remain.pop();
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
commandFunc = command && mout.object.get(bower.commands, command);
|
||||
command = command && command.replace(/\./g, ' ');
|
||||
|
||||
// If no command was specified, show bower help
|
||||
// Do the same if the command is unknown
|
||||
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)
|
||||
} else if (options.help || !commandFunc.line) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
// Call the line method
|
||||
} else {
|
||||
logger = commandFunc.line(process.argv);
|
||||
|
||||
// If the method failed to interpret the process arguments
|
||||
// show the command help
|
||||
if (!logger) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
}
|
||||
}
|
||||
|
||||
// Get the renderer and configure it with the executed command
|
||||
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' || 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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.'
|
||||
);
|
||||
}
|
||||
|
||||
if (bower.config.interactive) {
|
||||
var updateNotifier = require('update-notifier');
|
||||
|
||||
// Check for newer version of Bower
|
||||
var notifier = updateNotifier({ pkg: { name: 'bower', version: version } });
|
||||
|
||||
if (notifier.update && levels.info >= loglevel) {
|
||||
notifier.notify();
|
||||
}
|
||||
}
|
||||
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');
|
||||
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,28 +25,30 @@ 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) {
|
||||
throw createError('No homepage set for ' + pkgMeta.name, 'ENOHOME');
|
||||
}
|
||||
|
||||
open(homepage);
|
||||
open(homepage, { wait: false });
|
||||
return homepage;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
home.readOptions = function (argv) {
|
||||
home.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
@@ -10,24 +10,24 @@ var config = require('../config');
|
||||
* return as soon as possible and load and execute the command asynchronously.
|
||||
*/
|
||||
function commandFactory(id) {
|
||||
if (process.env.STRICT_REQUIRE) {
|
||||
require(id);
|
||||
}
|
||||
|
||||
function command() {
|
||||
function runApi() {
|
||||
var command = require(id);
|
||||
var commandArgs = [].slice.call(arguments);
|
||||
|
||||
return withLogger(function (logger) {
|
||||
return withLogger(function(logger) {
|
||||
commandArgs.unshift(logger);
|
||||
return require(id).apply(undefined, commandArgs);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
});
|
||||
}
|
||||
|
||||
function runFromArgv(argv) {
|
||||
return withLogger(function (logger) {
|
||||
var command = require(id);
|
||||
var commandArgs;
|
||||
var command = require(id);
|
||||
|
||||
var commandArgs = command.readOptions(argv);
|
||||
commandArgs = command.readOptions(argv);
|
||||
|
||||
return withLogger(function(logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
@@ -37,30 +37,31 @@ 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;
|
||||
}
|
||||
|
||||
command.line = runFromArgv;
|
||||
runApi.line = runFromArgv;
|
||||
|
||||
return command;
|
||||
return runApi;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
cache: {
|
||||
clean: commandFactory('./cache/clean'),
|
||||
list: commandFactory('./cache/list'),
|
||||
list: commandFactory('./cache/list')
|
||||
},
|
||||
help: commandFactory('./help'),
|
||||
home: commandFactory('./home'),
|
||||
|
||||
@@ -2,7 +2,6 @@ var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function info(logger, endpoint, property, config) {
|
||||
@@ -10,22 +9,27 @@ 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;
|
||||
var tracker;
|
||||
|
||||
config = defaultConfig(config);
|
||||
repository = new PackageRepository(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
|
||||
decEndpoint = endpointParser.decompose(endpoint);
|
||||
tracker.trackDecomposedEndpoints('info', [decEndpoint]);
|
||||
|
||||
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,
|
||||
@@ -39,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,79 +193,76 @@ 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': 'moduleType',
|
||||
'message': 'what types of modules does this package expose?',
|
||||
'type': 'checkbox',
|
||||
'choices': ['amd', 'es6', 'globals', 'node', 'yui']
|
||||
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;
|
||||
json.moduleType = answers.moduleType;
|
||||
json.keywords = toArray(answers.keywords);
|
||||
json.authors = toArray(answers.authors, ',');
|
||||
json.license = answers.license;
|
||||
@@ -256,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;
|
||||
});
|
||||
|
||||
@@ -285,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
|
||||
@@ -299,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);
|
||||
});
|
||||
}
|
||||
@@ -313,7 +335,7 @@ function setDependencies(project, json, answers) {
|
||||
|
||||
// -------------------
|
||||
|
||||
init.readOptions = function (argv) {
|
||||
init.readOptions = function(argv) {
|
||||
return [];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Project = require('../core/Project');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function install(logger, endpoints, options, config) {
|
||||
var project;
|
||||
var decEndpoints;
|
||||
var tracker;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
@@ -14,30 +12,38 @@ function install(logger, endpoints, options, config) {
|
||||
options.save = config.defaultSave;
|
||||
}
|
||||
project = new Project(config, logger);
|
||||
tracker = new Tracker(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);
|
||||
});
|
||||
tracker.trackDecomposedEndpoints('install', decEndpoints);
|
||||
|
||||
return project.install(decEndpoints, 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);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var createLink = require('../util/createLink');
|
||||
var defaultConfig = require('../config');
|
||||
var relativeToBaseDir = require('../util/relativeToBaseDir');
|
||||
|
||||
function link(logger, name, localName, config) {
|
||||
if (name) {
|
||||
@@ -19,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
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,30 +51,32 @@ function linkTo(logger, name, localName, config) {
|
||||
|
||||
localName = localName || name;
|
||||
src = path.join(config.storage.links, name);
|
||||
dst = path.join(config.cwd, config.directory, localName);
|
||||
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,64 +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);
|
||||
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);
|
||||
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;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var Q = require('q');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function lookup(logger, name, config) {
|
||||
@@ -7,27 +7,26 @@ function lookup(logger, name, config) {
|
||||
return new Q(null);
|
||||
}
|
||||
|
||||
var registryClient;
|
||||
|
||||
config = defaultConfig(config);
|
||||
config.cache = config.storage.registry;
|
||||
|
||||
registryClient = new RegistryClient(config, logger);
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), name)
|
||||
.then(function (entry) {
|
||||
// TODO: Handle entry.type.. for now it's only 'alias'
|
||||
// When we got published packages, this needs to be adjusted
|
||||
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;
|
||||
|
||||
|
||||
@@ -1,79 +1,93 @@
|
||||
var Q = require('q');
|
||||
var chalk = require('chalk');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
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 tracker;
|
||||
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;
|
||||
tracker = new Tracker(config);
|
||||
|
||||
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'
|
||||
);
|
||||
}
|
||||
|
||||
tracker.track('register');
|
||||
|
||||
// Attempt to resolve the package referenced by the URL to ensure
|
||||
// everything is ok before registering
|
||||
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);
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
var Q = require('q');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var defaultConfig = require('../config');
|
||||
var cli = require('../util/cli');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function search(logger, name, config) {
|
||||
var registryClient;
|
||||
var tracker;
|
||||
|
||||
config = defaultConfig(config);
|
||||
config.cache = config.storage.registry;
|
||||
|
||||
registryClient = new RegistryClient(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
tracker.track('search', name);
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
|
||||
// If no name was specified, list all packages
|
||||
if (!name) {
|
||||
return Q.nfcall(registryClient.list.bind(registryClient));
|
||||
// Otherwise search it
|
||||
} else {
|
||||
if (name) {
|
||||
return Q.nfcall(registryClient.search.bind(registryClient), name);
|
||||
} else {
|
||||
// List all packages when in interactive mode + json enabled, and
|
||||
// always when in non-interactive mode
|
||||
if (config.interactive && !config.json) {
|
||||
throw createError('no parameter to bower search', 'EREADOPTIONS');
|
||||
}
|
||||
|
||||
return Q.nfcall(registryClient.list.bind(registryClient));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
search.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
search.readOptions = function(argv) {
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain.slice(1).join(' ');
|
||||
var terms = options.argv.remain.slice(1);
|
||||
|
||||
var name = terms.join(' ');
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function uninstall(logger, names, options, config) {
|
||||
@@ -10,63 +9,67 @@ function uninstall(logger, names, options, config) {
|
||||
}
|
||||
|
||||
var project;
|
||||
var tracker;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
|
||||
tracker.trackNames('uninstall', names);
|
||||
|
||||
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];
|
||||
});
|
||||
|
||||
@@ -76,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);
|
||||
|
||||
|
||||
@@ -3,23 +3,19 @@ var Q = require('q');
|
||||
|
||||
var defaultConfig = require('../config');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function unregister(logger, name, config) {
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
var repository;
|
||||
var registryClient;
|
||||
var tracker;
|
||||
var force;
|
||||
|
||||
config = defaultConfig(config);
|
||||
force = config.force;
|
||||
tracker = new Tracker(config);
|
||||
|
||||
// Bypass any cache
|
||||
config.offline = false;
|
||||
@@ -30,50 +26,59 @@ function unregister(logger, name, config) {
|
||||
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
tracker.track('unregister');
|
||||
|
||||
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) {
|
||||
tracker.track('unregistered');
|
||||
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];
|
||||
};
|
||||
|
||||
@@ -20,44 +20,40 @@ function readCachedConfig(cwd, overwrites) {
|
||||
|
||||
object.mixIn(config, configstore);
|
||||
|
||||
// Delete the json attribute because it is no longer supported
|
||||
// and conflicts with --json
|
||||
delete config.json;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
function restoreConfig () {
|
||||
function restoreConfig() {
|
||||
if (current) {
|
||||
current.restore();
|
||||
}
|
||||
}
|
||||
|
||||
function resetCache () {
|
||||
function resetCache() {
|
||||
restoreConfig();
|
||||
current = undefined;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
var ResolveCache = require('./ResolveCache');
|
||||
var resolverFactory = require('./resolverFactory');
|
||||
var createError = require('../util/createError');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
|
||||
function PackageRepository(config, logger) {
|
||||
var registryOptions;
|
||||
@@ -14,6 +14,7 @@ function PackageRepository(config, logger) {
|
||||
// Instantiate the registry
|
||||
registryOptions = mout.object.deepMixIn({}, this._config);
|
||||
registryOptions.cache = this._config.storage.registry;
|
||||
|
||||
this._registryClient = new RegistryClient(registryOptions, logger);
|
||||
|
||||
// Instantiate the resolve cache
|
||||
@@ -22,7 +23,7 @@ function PackageRepository(config, logger) {
|
||||
|
||||
// -----------------
|
||||
|
||||
PackageRepository.prototype.fetch = function (decEndpoint) {
|
||||
PackageRepository.prototype.fetch = function(decEndpoint) {
|
||||
var logger;
|
||||
var that = this;
|
||||
var isTargetable;
|
||||
@@ -34,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();
|
||||
@@ -167,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
|
||||
|
||||
1277
lib/core/Project.js
1277
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;
|
||||
|
||||
@@ -4,17 +4,24 @@ var path = require('path');
|
||||
var mout = require('mout');
|
||||
var resolvers = require('./resolvers');
|
||||
var createError = require('../util/createError');
|
||||
var resolve = require('../util/resolve');
|
||||
|
||||
var pluginResolverFactory = require('./resolvers/pluginResolverFactory');
|
||||
|
||||
function createInstance(decEndpoint, options, registryClient) {
|
||||
decEndpoint = mout.object.pick(decEndpoint, ['name', 'target', 'source']);
|
||||
|
||||
options.version = require('../../package.json').version;
|
||||
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
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,12 +31,12 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
|
||||
// Below we try a series of async tests to guess the type of resolver to use
|
||||
// If a step was unable to guess the resolver, it returns undefined
|
||||
// If a step can guess the resolver, it returns with construcotor of resolver
|
||||
// If a step can guess the resolver, it returns with constructor of resolver
|
||||
|
||||
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 {
|
||||
@@ -42,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;
|
||||
|
||||
@@ -54,17 +61,32 @@ function getConstructor(decEndpoint, options, registryClient) {
|
||||
resolverNames = [];
|
||||
}
|
||||
|
||||
var resolverPromises = resolverNames.map(function (resolverName) {
|
||||
var resolver = resolvers[resolverName]
|
||||
|| pluginResolverFactory(require(resolverName), options);
|
||||
var resolverPromises = resolverNames.map(function(resolverName) {
|
||||
var resolver = resolvers[resolverName];
|
||||
|
||||
return function () {
|
||||
if (resolver === undefined) {
|
||||
var resolverPath = resolve(resolverName, { cwd: config.cwd });
|
||||
|
||||
if (resolverPath === undefined) {
|
||||
throw createError(
|
||||
'Bower resolver not found: ' + resolverName,
|
||||
'ENORESOLVER'
|
||||
);
|
||||
}
|
||||
|
||||
resolver = pluginResolverFactory(
|
||||
require(resolverPath),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -73,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)) {
|
||||
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
|
||||
@@ -105,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;
|
||||
@@ -172,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;
|
||||
@@ -206,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,9 +29,6 @@ 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://');
|
||||
@@ -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(this._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
|
||||
|
||||
@@ -27,6 +27,18 @@ function GitResolver(decEndpoint, config, logger) {
|
||||
mkdirp.sync(config.storage.empty);
|
||||
process.env.GIT_TEMPLATE_DIR = config.storage.empty;
|
||||
|
||||
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);
|
||||
|
||||
if (!hasGit) {
|
||||
@@ -39,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;
|
||||
}
|
||||
|
||||
@@ -59,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;
|
||||
@@ -96,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
|
||||
@@ -117,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
|
||||
@@ -222,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;
|
||||
@@ -234,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
|
||||
@@ -287,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
|
||||
@@ -319,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
|
||||
@@ -353,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();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ var rimraf = require('../../util/rimraf');
|
||||
var readJson = require('../../util/readJson');
|
||||
var createError = require('../../util/createError');
|
||||
var removeIgnores = require('../../util/removeIgnores');
|
||||
var md5 = require('md5-hex');
|
||||
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
@@ -23,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;
|
||||
|
||||
@@ -57,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
|
||||
@@ -73,90 +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, 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', 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', 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', 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', 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'])
|
||||
.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'])
|
||||
.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,58 +57,78 @@ 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._download = function () {
|
||||
var fileName = url.parse(path.basename(this._source)).pathname;
|
||||
UrlResolver.prototype._parseSourceURL = function(_url) {
|
||||
return url.parse(path.basename(_url)).pathname;
|
||||
};
|
||||
|
||||
UrlResolver.prototype._download = function() {
|
||||
var fileName = this._parseSourceURL(this._source);
|
||||
|
||||
if (!fileName) {
|
||||
this._source = this._source.replace(/\/(?=\?|#)/, '');
|
||||
fileName = this._parseSourceURL(this._source);
|
||||
}
|
||||
|
||||
var file = path.join(this._tempDir, fileName);
|
||||
var reqHeaders = {};
|
||||
var that = this;
|
||||
@@ -126,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;
|
||||
@@ -186,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)) {
|
||||
@@ -216,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
|
||||
@@ -256,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,11 +51,11 @@ 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 || '*';
|
||||
};
|
||||
|
||||
@@ -62,7 +69,7 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
}
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getPkgMeta = function () {
|
||||
PluginResolver.prototype.getPkgMeta = function() {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
@@ -76,42 +83,46 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
|
||||
// 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
|
||||
if(that.constructor.isTargetable()) {
|
||||
if (that.constructor.isTargetable()) {
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -280,34 +334,45 @@ function pluginResolverFactory(resolverFactory, bower) {
|
||||
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]) {
|
||||
/*jshint newcap: false */
|
||||
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 cmdString = mout.string.replace(config.scripts[action], '%', orderedPackages.join(' '));
|
||||
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(' ')
|
||||
);
|
||||
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
|
||||
|
||||
30
lib/index.js
30
lib/index.js
@@ -1,30 +1,6 @@
|
||||
var abbrev = require('abbrev');
|
||||
var mout = require('mout');
|
||||
var commands = require('./commands');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
var abbreviations = abbrev(expandNames(commands));
|
||||
abbreviations.i = 'install';
|
||||
abbreviations.rm = 'uninstall';
|
||||
abbreviations.unlink = 'uninstall';
|
||||
abbreviations.ls = 'list';
|
||||
|
||||
function expandNames(obj, prefix, stack) {
|
||||
prefix = prefix || '';
|
||||
stack = stack || [];
|
||||
|
||||
mout.object.forOwn(obj, function (value, name) {
|
||||
name = prefix + name;
|
||||
|
||||
stack.push(name);
|
||||
|
||||
if (typeof value === 'object' && !value.line) {
|
||||
expandNames(value, name + ' ', stack);
|
||||
}
|
||||
});
|
||||
|
||||
return stack;
|
||||
}
|
||||
var version = require('./version');
|
||||
var abbreviations = require('./util/abbreviations')(commands);
|
||||
|
||||
function clearRuntimeCache() {
|
||||
// Note that in edge cases, some architecture components instance's
|
||||
@@ -35,7 +11,7 @@ function clearRuntimeCache() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
version: pkg.version,
|
||||
version: version,
|
||||
commands: commands,
|
||||
config: require('./config')(),
|
||||
abbreviations: abbreviations,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -31,16 +31,14 @@ JsonRenderer.prototype.error = function (err) {
|
||||
err.message = message;
|
||||
|
||||
// Stack
|
||||
/*jshint camelcase:false*/
|
||||
stack = err.fstream_stack || err.stack || 'N/A';
|
||||
err.stacktrace = (Array.isArray(stack) ? stack.join('\n') : stack);
|
||||
/*jshint camelcase:true*/
|
||||
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 {
|
||||
@@ -51,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;
|
||||
|
||||
@@ -65,18 +63,20 @@ 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
|
||||
@@ -92,38 +92,39 @@ JsonRenderer.prototype.prompt = function (prompts) {
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
default:
|
||||
promise = promise.then(function () {
|
||||
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..
|
||||
|
||||
@@ -5,14 +5,15 @@ var archy = require('archy');
|
||||
var Q = require('q');
|
||||
var stringifyObject = require('stringify-object');
|
||||
var os = require('os');
|
||||
var pkg = require(path.join(__dirname, '../..', 'package.json'));
|
||||
var semverUtils = require('semver-utils');
|
||||
var version = require('../version');
|
||||
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,
|
||||
@@ -31,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);
|
||||
}
|
||||
@@ -42,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]) {
|
||||
@@ -50,7 +51,7 @@ StandardRenderer.prototype.end = function (data) {
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.error = function (err) {
|
||||
StandardRenderer.prototype.error = function(err) {
|
||||
var str;
|
||||
var stack;
|
||||
|
||||
@@ -59,37 +60,48 @@ 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);
|
||||
}
|
||||
|
||||
// Print trace if verbose, the error has no code
|
||||
// or if the error is a node error
|
||||
if (this._config.verbose || !err.code || err.errno) {
|
||||
/*jshint camelcase:false*/
|
||||
stack = err.fstream_stack || err.stack || 'N/A';
|
||||
str = chalk.yellow('\nStack trace:\n');
|
||||
str += (Array.isArray(stack) ? stack.join('\n') : stack) + '\n';
|
||||
str += chalk.yellow('\nConsole trace:\n');
|
||||
/*jshint camelcase:true*/
|
||||
|
||||
this._write(process.stderr, str);
|
||||
this._write(process.stderr, new Error().stack);
|
||||
|
||||
// Print bower version, node version and system info.
|
||||
this._write(process.stderr, chalk.yellow('\nSystem info:\n'));
|
||||
this._write(process.stderr, 'Bower version: ' + pkg.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, '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'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.log = function (log) {
|
||||
StandardRenderer.prototype.log = function(log) {
|
||||
var method = '_' + mout.string.camelCase(log.id) + 'Log';
|
||||
|
||||
this._guessOrigin(log);
|
||||
@@ -102,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);
|
||||
});
|
||||
}
|
||||
@@ -122,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;
|
||||
@@ -137,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;
|
||||
@@ -209,19 +229,32 @@ StandardRenderer.prototype._info = function (data) {
|
||||
|
||||
// Render the versions at the end
|
||||
if (includeVersions) {
|
||||
data.hidePreReleases = false;
|
||||
data.numPreReleases = 0;
|
||||
// If output isn't verbose, hide prereleases
|
||||
if (!this._config.verbose) {
|
||||
data.versions = mout.array.filter(data.versions, function(version) {
|
||||
version = semverUtils.parse(version);
|
||||
if (!version.release && !version.build) {
|
||||
return true;
|
||||
}
|
||||
data.numPreReleases++;
|
||||
});
|
||||
data.hidePreReleases = !!data.numPreReleases;
|
||||
}
|
||||
str += '\n' + template.render('std/info.std', 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({
|
||||
@@ -236,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
|
||||
@@ -248,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;
|
||||
|
||||
@@ -272,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;
|
||||
}
|
||||
@@ -280,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;
|
||||
}
|
||||
@@ -288,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;
|
||||
}
|
||||
@@ -296,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');
|
||||
@@ -316,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;
|
||||
}
|
||||
@@ -334,7 +377,7 @@ StandardRenderer.prototype._cachedEntryLog = function (log) {
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._guessOrigin = function (log) {
|
||||
StandardRenderer.prototype._guessOrigin = function(log) {
|
||||
var data = log.data;
|
||||
|
||||
if (!data) {
|
||||
@@ -342,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) {
|
||||
@@ -361,7 +405,7 @@ StandardRenderer.prototype._guessOrigin = function (log) {
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._prefix = function (log) {
|
||||
StandardRenderer.prototype._prefix = function(log) {
|
||||
var label;
|
||||
var length;
|
||||
var nrSpaces;
|
||||
@@ -393,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);
|
||||
}
|
||||
@@ -401,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);
|
||||
}
|
||||
}
|
||||
@@ -421,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;
|
||||
|
||||
@@ -446,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');
|
||||
}
|
||||
@@ -460,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;
|
||||
}
|
||||
|
||||
@@ -488,7 +535,7 @@ StandardRenderer._wideCommands = [
|
||||
'register'
|
||||
];
|
||||
StandardRenderer._idMappings = {
|
||||
'mutual': 'conflict',
|
||||
mutual: 'conflict',
|
||||
'cached-entry': 'cached'
|
||||
};
|
||||
|
||||
|
||||
13
lib/templates/helpers/colors.js
Normal file
13
lib/templates/helpers/colors.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var chalk = require('chalk');
|
||||
|
||||
var templateColors = ['yellow', 'green', 'cyan', 'red', 'white', 'magenta'];
|
||||
|
||||
function colors(Handlebars) {
|
||||
templateColors.forEach(function(color) {
|
||||
Handlebars.registerHelper(color, function(context) {
|
||||
return chalk[color](context.fn(this));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = colors;
|
||||
@@ -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));
|
||||
|
||||
12
lib/templates/helpers/rpad.js
Normal file
12
lib/templates/helpers/rpad.js
Normal file
@@ -0,0 +1,12 @@
|
||||
var mout = require('mout');
|
||||
|
||||
function rpad(Handlebars) {
|
||||
Handlebars.registerHelper('rpad', function(context) {
|
||||
var hash = context.hash;
|
||||
var minLength = parseInt(hash.minLength, 10);
|
||||
var chr = hash.char;
|
||||
return mout.string.rpad(context.fn(this), minLength, chr);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = rpad;
|
||||
@@ -1,5 +1,5 @@
|
||||
function sum(Handlebars) {
|
||||
Handlebars.registerHelper('sum', function (val1, val2) {
|
||||
Handlebars.registerHelper('sum', function(val1, val2) {
|
||||
return val1 + val2;
|
||||
});
|
||||
}
|
||||
@@ -11,6 +11,11 @@
|
||||
"flag": "--force-latest",
|
||||
"description": "Force latest version on conflict"
|
||||
},
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "If dependencies are installed, it reinstalls all installed components. It also forces installation even when there are non-bower directories with the same name in the components directory. Also bypasses the cache and overwrites to the cache anyway."
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
@@ -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>]"
|
||||
],
|
||||
@@ -5,6 +5,11 @@
|
||||
"register <name> <url> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Bypasses confirmation. Bower login is still needed."
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"command": "search",
|
||||
"description": "Finds all packages or a specific package.",
|
||||
"description": "Search for packages by name.",
|
||||
"usage": [
|
||||
"search [<options>]",
|
||||
"search <name> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
@@ -5,6 +5,11 @@
|
||||
"uninstall <name> [<name> ..] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Continues uninstallation even after a dependency conflict"
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
@@ -5,6 +5,11 @@
|
||||
"unregister <name> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Bypasses confirmation. Bower login is still needed."
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
@@ -19,6 +19,16 @@
|
||||
"shorthand": "-p",
|
||||
"flag": "--production",
|
||||
"description": "Do not install project devDependencies"
|
||||
},
|
||||
{
|
||||
"shorthand": "-S",
|
||||
"flag": "--save",
|
||||
"description": "Update dependencies in bower.json"
|
||||
},
|
||||
{
|
||||
"shorthand": "-D",
|
||||
"flag": "--save-dev",
|
||||
"description": "Update devDependencies in bower.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
lib/templates/json/help-version.json
Normal file
14
lib/templates/json/help-version.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"command": "version",
|
||||
"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 [<version> | major | minor | patch]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-m",
|
||||
"flag": "--message",
|
||||
"description": "Custom git commit and tag message"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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",
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"shorthand": "-l",
|
||||
"flag": "--log-level",
|
||||
"flag": "--loglevel",
|
||||
"description": "What level of logs to report"
|
||||
},
|
||||
{
|
||||
@@ -69,6 +69,10 @@
|
||||
{
|
||||
"flag": "--no-color",
|
||||
"description": "Disable colors"
|
||||
},
|
||||
{
|
||||
"flag": "--config.interactive=false",
|
||||
"description": "Disable prompts"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#yellow}}Unable to find a suitable version for {{name}}, please choose one:{{/yellow}}
|
||||
{{#yellow}}Unable to find a suitable version for {{name}}, please choose one by typing one of the numbers below:{{/yellow}}
|
||||
{{#condense}}
|
||||
{{#each picks}}
|
||||
{{#magenta}}{{sum @index 1}}){{/magenta}} {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#green}}{{pkgMeta._release}}{{/green}}{{/if}}{{#if dependants}} and is required by {{#green}}{{dependants}}{{/green}}{{/if}}
|
||||
@@ -11,7 +11,7 @@ Commands:
|
||||
|
||||
{{#condense}}
|
||||
{{#each commands}}
|
||||
{{#rpad length="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{#rpad minLength="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
{{#rpad minLength="23"}}{{/rpad}}
|
||||
@@ -7,11 +7,12 @@ Usage:
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
|
||||
Options:
|
||||
|
||||
{{#condense}}
|
||||
{{#each options}}
|
||||
{{#yellow}}{{#rpad length="23"}}{{#if shorthand}}{{shorthand}}, {{/if}}{{flag}}{{/rpad}}{{/yellow}} {{description}}
|
||||
{{#yellow}}{{#rpad minLength="23"}}{{#if shorthand}}{{shorthand}}, {{/if}}{{flag}}{{/rpad}}{{/yellow}} {{description}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
@@ -11,7 +11,7 @@ Commands:
|
||||
|
||||
{{#condense}}
|
||||
{{#each commands}}
|
||||
{{#rpad length="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{#rpad minLength="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
@@ -19,7 +19,7 @@ Options:
|
||||
|
||||
{{#condense}}
|
||||
{{#each options}}
|
||||
{{#yellow}}{{#rpad length="23"}}{{#if shorthand}}{{shorthand}}, {{/if}}{{flag}}{{/rpad}}{{/yellow}} {{description}}
|
||||
{{#yellow}}{{#rpad minLength="23"}}{{#if shorthand}}{{shorthand}}, {{/if}}{{flag}}{{/rpad}}{{/yellow}} {{description}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
{{/versions}}
|
||||
{{/condense}}
|
||||
|
||||
|
||||
{{#if hidePreReleases}}
|
||||
Show {{numPreReleases}} additional prereleases with ‘bower info {{name}} --verbose’
|
||||
{{/if}}
|
||||
You can request info for a specific version with 'bower info {{name}}#<version>'
|
||||
{{else}}No versions available.
|
||||
{{/if}}
|
||||
30
lib/util/abbreviations.js
Normal file
30
lib/util/abbreviations.js
Normal file
@@ -0,0 +1,30 @@
|
||||
var abbrev = require('abbrev');
|
||||
var mout = require('mout');
|
||||
|
||||
function expandNames(obj, prefix, stack) {
|
||||
prefix = prefix || '';
|
||||
stack = stack || [];
|
||||
|
||||
mout.object.forOwn(obj, function(value, name) {
|
||||
name = prefix + name;
|
||||
|
||||
stack.push(name);
|
||||
|
||||
if (typeof value === 'object' && !value.line) {
|
||||
expandNames(value, name + ' ', stack);
|
||||
}
|
||||
});
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
module.exports = function(commands) {
|
||||
var abbreviations = abbrev(expandNames(commands));
|
||||
|
||||
abbreviations.i = 'install';
|
||||
abbreviations.rm = 'uninstall';
|
||||
abbreviations.unlink = 'uninstall';
|
||||
abbreviations.ls = 'list';
|
||||
|
||||
return abbreviations;
|
||||
};
|
||||
@@ -1,127 +0,0 @@
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
|
||||
var analytics = module.exports;
|
||||
|
||||
var insight;
|
||||
|
||||
var enableAnalytics = false;
|
||||
|
||||
// Insight takes long to load, and often causes problems
|
||||
// in non-interactive environment, so we load it lazily
|
||||
//
|
||||
// Insight is used in two cases:
|
||||
//
|
||||
// 1. Read insight configuration (whether track user actions)
|
||||
// 2. Track user actions (Tracker.track method)
|
||||
//
|
||||
// We don't want to instantiate Insight in non-interactive mode
|
||||
// because it takes time to read config and configstore has concurrency issues:
|
||||
//
|
||||
// https://github.com/yeoman/configstore/issues/20
|
||||
|
||||
function ensureInsight () {
|
||||
if (!insight) {
|
||||
var Insight = require('insight');
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
insight = new Insight({
|
||||
trackingCode: 'UA-00000000-0',
|
||||
pkg: {
|
||||
name: 'bower-test',
|
||||
version: '1.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
insight.config.clear();
|
||||
} else {
|
||||
insight = new Insight({
|
||||
trackingCode: 'UA-43531210-1',
|
||||
pkg: require('../../package.json')
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the application-wide insight singleton and asks for the
|
||||
// permission on the CLI during the first run.
|
||||
//
|
||||
// This method is called only from bin/bower. Programmatic API skips it.
|
||||
analytics.setup = function setup (config) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
// No need for asking if analytics is set in bower config
|
||||
if (config.analytics === undefined) {
|
||||
ensureInsight();
|
||||
|
||||
// For non-interactive call from bin/bower we disable analytics
|
||||
if (config.interactive) {
|
||||
if (insight.optOut !== undefined) {
|
||||
deferred.resolve(!insight.optOut);
|
||||
} else {
|
||||
insight.askPermission(null, function(err, optIn) {
|
||||
// optIn callback param was exactly opposite before 0.4.3
|
||||
// so we force at least insight@0.4.3 in package.json
|
||||
deferred.resolve(optIn);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// no specified value, no stored value, and can't prompt for one
|
||||
// most likely CI environment; defaults to false to reduce data noise
|
||||
deferred.resolve(false);
|
||||
}
|
||||
} else {
|
||||
// use the specified value
|
||||
deferred.resolve(config.analytics);
|
||||
}
|
||||
|
||||
return deferred.promise.then(function (enabled) {
|
||||
enableAnalytics = enabled;
|
||||
|
||||
return enabled;
|
||||
});
|
||||
};
|
||||
|
||||
var Tracker = analytics.Tracker = function Tracker(config) {
|
||||
function analyticsEnabled () {
|
||||
// Allow for overriding analytics default
|
||||
if (config && config.analytics !== undefined) {
|
||||
return config.analytics;
|
||||
}
|
||||
|
||||
// TODO: let bower pass this variable from bin/bower instead closure
|
||||
return enableAnalytics;
|
||||
}
|
||||
|
||||
if (analyticsEnabled()) {
|
||||
ensureInsight();
|
||||
} else {
|
||||
this.track = function noop () {};
|
||||
this.trackDecomposedEndpoints = function noop () {};
|
||||
this.trackPackages = function noop () {};
|
||||
this.trackNames = function noop () {};
|
||||
}
|
||||
};
|
||||
|
||||
Tracker.prototype.track = function track() {
|
||||
insight.track.apply(insight, arguments);
|
||||
};
|
||||
|
||||
Tracker.prototype.trackDecomposedEndpoints = function trackDecomposedEndpoints(command, endpoints) {
|
||||
endpoints.forEach(function (endpoint) {
|
||||
this.track(command, endpoint.source, endpoint.target);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Tracker.prototype.trackPackages = function trackPackages(command, packages) {
|
||||
mout.object.forOwn(packages, function (package) {
|
||||
var meta = package.pkgMeta;
|
||||
this.track(command, meta.name, meta.version);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Tracker.prototype.trackNames = function trackNames(command, names) {
|
||||
names.forEach(function (name) {
|
||||
this.track(command, name);
|
||||
}.bind(this));
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -5,12 +5,14 @@ var mout = require('mout');
|
||||
var retry = require('retry');
|
||||
var createError = require('./createError');
|
||||
var createWriteStream = require('fs-write-stream-atomic');
|
||||
var destroy = require('destroy');
|
||||
|
||||
var errorCodes = [
|
||||
'EADDRINFO',
|
||||
'ETIMEDOUT',
|
||||
'ECONNRESET',
|
||||
'ESOCKETTIMEDOUT'
|
||||
'ESOCKETTIMEDOUT',
|
||||
'ENOTFOUND'
|
||||
];
|
||||
|
||||
function download(url, file, options) {
|
||||
@@ -18,105 +20,132 @@ 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
|
||||
}, 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 () {
|
||||
Q.fcall(fetch, url, file, {
|
||||
progressDelay: progressDelay
|
||||
}).then(function(response) {
|
||||
deferred.resolve(response);
|
||||
}).fail(function (error) {
|
||||
// Save timeout before retrying to report
|
||||
var timeout = operation._timeouts[0];
|
||||
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];
|
||||
|
||||
// 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;
|
||||
|
||||
deferred.notify({
|
||||
retry: true,
|
||||
delay: timeout,
|
||||
error: 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);
|
||||
}
|
||||
});
|
||||
|
||||
operation.retry(error);
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function fetch(url, file, options) {
|
||||
var response;
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Retry on network errors
|
||||
var req;
|
||||
var writeStream;
|
||||
var contentLength;
|
||||
var bytesDownloaded = 0;
|
||||
|
||||
req = progress(request(url, options), {
|
||||
var reject = function(error) {
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
var req = progress(request(url, options), {
|
||||
delay: options.progressDelay
|
||||
})
|
||||
.on('response', function (res) {
|
||||
var status = res.statusCode;
|
||||
.on('response', function(response) {
|
||||
contentLength = Number(response.headers['content-length']);
|
||||
|
||||
if (status < 200 || status >= 300) {
|
||||
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
|
||||
}
|
||||
var status = response.statusCode;
|
||||
|
||||
response = res;
|
||||
contentLength = Number(res.headers['content-length']);
|
||||
})
|
||||
.on('data', function (data) {
|
||||
bytesDownloaded += data.length;
|
||||
})
|
||||
.on('progress', function (state) {
|
||||
deferred.notify(state);
|
||||
})
|
||||
.on('error', function (error) {
|
||||
deferred.reject(error);
|
||||
})
|
||||
.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'
|
||||
));
|
||||
}
|
||||
});
|
||||
if (status < 200 || status >= 300) {
|
||||
return deferred.reject(
|
||||
createError('Status code of ' + status, 'EHTTP')
|
||||
);
|
||||
}
|
||||
|
||||
writeStream = createWriteStream(file);
|
||||
var writeStream = createWriteStream(file);
|
||||
var errored = false;
|
||||
|
||||
// Pipe read stream to write stream
|
||||
writeStream.on('error', function (error) {
|
||||
writeStream.destroy();
|
||||
deferred.reject(error);
|
||||
});
|
||||
// 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);
|
||||
|
||||
writeStream.on('finish', function () {
|
||||
deferred.resolve(response);
|
||||
});
|
||||
// Wait for writeStream to cleanup after itself...
|
||||
// TODO: Maybe there's a better way?
|
||||
setTimeout(function() {
|
||||
deferred.reject(error);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
req.pipe(writeStream);
|
||||
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'
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ var junk = require('junk');
|
||||
var createError = require('./createError');
|
||||
var createWriteStream = require('fs-write-stream-atomic');
|
||||
var destroy = require('destroy');
|
||||
var tmp = require('tmp');
|
||||
|
||||
// This forces the default chunk size to something small in an attempt
|
||||
// to avoid issue #314
|
||||
@@ -37,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;
|
||||
}
|
||||
@@ -53,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;
|
||||
}
|
||||
@@ -78,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;
|
||||
}
|
||||
@@ -105,19 +112,20 @@ 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;
|
||||
}
|
||||
@@ -135,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);
|
||||
});
|
||||
|
||||
@@ -143,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
|
||||
@@ -157,32 +164,27 @@ 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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function moveSingleDirContents(dir) {
|
||||
var destDir = path.dirname(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);
|
||||
|
||||
return Q.nfcall(fs.readdir, dir)
|
||||
.then(function (files) {
|
||||
var promises;
|
||||
return Q.nfcall(fs.rename, src, dst);
|
||||
});
|
||||
|
||||
promises = files.map(function (file) {
|
||||
var src = path.join(dir, file);
|
||||
var dst = path.join(destDir, file);
|
||||
|
||||
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, dir);
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
@@ -212,48 +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'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 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, dst);
|
||||
});
|
||||
|
||||
// TODO: There's an issue here if the src and dst are the same and
|
||||
// The zip name is the same as some of the zip file contents
|
||||
// Maybe create a temp directory inside dst, unzip it there,
|
||||
// unlink zip and then move contents
|
||||
|
||||
// Remove archive
|
||||
if (!opts.keepArchive) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.unlink, src);
|
||||
});
|
||||
}
|
||||
|
||||
// Move contents if a single directory was extracted
|
||||
if (!opts.keepStructure) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return isSingleDir(dst);
|
||||
// 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 (singleDir) {
|
||||
return singleDir ? moveSingleDirContents(singleDir) : null;
|
||||
});
|
||||
}
|
||||
.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.lstat(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,8 +21,8 @@ module.exports.readdir = function (dir, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.readdirSync = function (dir) {
|
||||
var stats = fs.lstatSync(dir);
|
||||
module.exports.readdirSync = function(dir) {
|
||||
var stats = fs.statSync(dir);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return readdirSync(dir);
|
||||
|
||||
5
lib/util/isPathAbsolute.js
Normal file
5
lib/util/isPathAbsolute.js
Normal file
@@ -0,0 +1,5 @@
|
||||
function isPathAbsolute(filePath) {
|
||||
return filePath.charAt(0) === '/';
|
||||
}
|
||||
|
||||
module.exports = isPathAbsolute;
|
||||
@@ -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;
|
||||
|
||||
14
lib/util/relativeToBaseDir.js
Normal file
14
lib/util/relativeToBaseDir.js
Normal file
@@ -0,0 +1,14 @@
|
||||
var path = require('path');
|
||||
var isPathAbsolute = require('./isPathAbsolute');
|
||||
|
||||
function relativeToBaseDir(baseDir) {
|
||||
return function(filePath) {
|
||||
if (isPathAbsolute(filePath)) {
|
||||
return path.resolve(filePath);
|
||||
} else {
|
||||
return path.resolve(baseDir, filePath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = relativeToBaseDir;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user