mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Compare commits
2481 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a20b4ba91d | ||
|
|
b123fed19b | ||
|
|
f6443b675a | ||
|
|
199e46c94a | ||
|
|
d3455643d2 | ||
|
|
95d5a35f70 | ||
|
|
7c922f577a | ||
|
|
7ae3808b44 | ||
|
|
f7b21d38a8 | ||
|
|
807eada63f | ||
|
|
c1abde9269 | ||
|
|
f82a541679 | ||
|
|
ce59f15cc9 | ||
|
|
845995fccd | ||
|
|
ebd99ad866 | ||
|
|
26a37942de | ||
|
|
390dec6d7f | ||
|
|
234ee9149d | ||
|
|
bcb1911268 | ||
|
|
c18aa260f3 | ||
|
|
9d5f7b7f61 | ||
|
|
71661c6460 | ||
|
|
67590f7728 | ||
|
|
4e68c23c87 | ||
|
|
fcc02c4200 | ||
|
|
d24e7be811 | ||
|
|
746fd3a669 | ||
|
|
031c7731a4 | ||
|
|
fa62654b9c | ||
|
|
fd2d448b7e | ||
|
|
997fb2b298 | ||
|
|
9c9f332ea0 | ||
|
|
1861fa4355 | ||
|
|
79a539ffb4 | ||
|
|
0608b26364 | ||
|
|
15e5b9709d | ||
|
|
67741b4bfe | ||
|
|
45c6bfa86f | ||
|
|
4f68fc7daa | ||
|
|
206046b271 | ||
|
|
43894f5149 | ||
|
|
6390815c5f | ||
|
|
e8b94ecbd0 | ||
|
|
51feb8f925 | ||
|
|
1c15deadc0 | ||
|
|
2aa1f27367 | ||
|
|
275601601a | ||
|
|
c22c09546e | ||
|
|
6bc778df32 | ||
|
|
451c60ec20 | ||
|
|
d6a18ae7ee | ||
|
|
b62faa19a6 | ||
|
|
50ee729ea2 | ||
|
|
bb17839bc2 | ||
|
|
5a6ae540f9 | ||
|
|
1935716660 | ||
|
|
add601795f | ||
|
|
8e34328466 | ||
|
|
c3e9c94833 | ||
|
|
dd19bafa37 | ||
|
|
74af42c176 | ||
|
|
a6308bf8f8 | ||
|
|
e1dc0105d2 | ||
|
|
ce210e4f16 | ||
|
|
e483e9bc2c | ||
|
|
b0c3859699 | ||
|
|
e6d1b2d82e | ||
|
|
d4345bb254 | ||
|
|
975f9bdcdb | ||
|
|
a969a9c557 | ||
|
|
6500b421ce | ||
|
|
0641167b96 | ||
|
|
0d03374dab | ||
|
|
765d8e739d | ||
|
|
0bd318de53 | ||
|
|
aa6b51edc0 | ||
|
|
2c2e5309fd | ||
|
|
b716bc4e3a | ||
|
|
bda400634c | ||
|
|
b01243ac3c | ||
|
|
89902a6919 | ||
|
|
80308a41a6 | ||
|
|
47cc2262e1 | ||
|
|
f7c5154490 | ||
|
|
cba4b2a4cd | ||
|
|
bdabf6a4e6 | ||
|
|
7896224384 | ||
|
|
3209cda975 | ||
|
|
38501a0b93 | ||
|
|
e60d236b25 | ||
|
|
044896e708 | ||
|
|
fc4c260de4 | ||
|
|
d405917b4a | ||
|
|
22bbb3fcaf | ||
|
|
8b6c8eeaa9 | ||
|
|
58ddb59104 | ||
|
|
f26fe38c30 | ||
|
|
9317bb6e33 | ||
|
|
2693bae8ed | ||
|
|
8368539a93 | ||
|
|
8947400487 | ||
|
|
44eeb60529 | ||
|
|
2309cd80c6 | ||
|
|
684ab0c0a6 | ||
|
|
1f6b32a48e | ||
|
|
e4f0295ef1 | ||
|
|
85e39cf190 | ||
|
|
16c134f0f9 | ||
|
|
bcbff32716 | ||
|
|
809117ea73 | ||
|
|
af448ba484 | ||
|
|
89069784bb | ||
|
|
57cc6f7a40 | ||
|
|
cea728c7bc | ||
|
|
f6be8e5e28 | ||
|
|
c6f7ec36de | ||
|
|
3235b3c0d3 | ||
|
|
6ca5d434a4 | ||
|
|
eb27ae8fdc | ||
|
|
353a399f75 | ||
|
|
c718541a4e | ||
|
|
1f4372299a | ||
|
|
e640d0ec5c | ||
|
|
2e5acfe076 | ||
|
|
19f3c53c70 | ||
|
|
26902aec27 | ||
|
|
08feaf2b0a | ||
|
|
db1ed1c08f | ||
|
|
3c2562ca0e | ||
|
|
b1f1b8fae3 | ||
|
|
f1c874b202 | ||
|
|
4757e7353f | ||
|
|
249ac47b9c | ||
|
|
dc59913098 | ||
|
|
343e6ac8bc | ||
|
|
e729829174 | ||
|
|
40e3ee091b | ||
|
|
8ee2d78779 | ||
|
|
7c54812ecf | ||
|
|
3251051d20 | ||
|
|
81052830f2 | ||
|
|
7b2fd5dcd1 | ||
|
|
bbc9b35cb1 | ||
|
|
3aebb34f1d | ||
|
|
8065e5c64a | ||
|
|
b8e6f36a91 | ||
|
|
12d41aeb8c | ||
|
|
8c624bbda6 | ||
|
|
7ee1686cf4 | ||
|
|
8e181c1792 | ||
|
|
9569a8074d | ||
|
|
e8e4c8fdbc | ||
|
|
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 | ||
|
|
52463dea09 | ||
|
|
8cf09f5444 | ||
|
|
20a6190ed8 | ||
|
|
402a9f3017 | ||
|
|
af09872fba | ||
|
|
2311d7dc44 | ||
|
|
b261bf8a76 | ||
|
|
140c6d963f | ||
|
|
0c5e457359 | ||
|
|
bfa4295606 | ||
|
|
8ac68ede5d | ||
|
|
a019f887e9 | ||
|
|
eba2c69308 | ||
|
|
8df5970300 | ||
|
|
db265d471f | ||
|
|
0bb1536c99 | ||
|
|
64eb7d598a | ||
|
|
df8e5a16be | ||
|
|
3ce2dd3989 | ||
|
|
99105fbb57 | ||
|
|
96f1e98859 | ||
|
|
d614b057c9 | ||
|
|
aafe02f3e9 | ||
|
|
0441e16bdb | ||
|
|
30a489535e | ||
|
|
ac88ece259 | ||
|
|
9b45c76744 | ||
|
|
059a5f83b7 | ||
|
|
cf5cd61995 | ||
|
|
a499cc5103 | ||
|
|
123779bbd1 | ||
|
|
5365c7b428 | ||
|
|
da961a9c42 | ||
|
|
8ff0e0e2d1 | ||
|
|
ffde6bd228 | ||
|
|
bebb4fb33b | ||
|
|
2c243ea5b7 | ||
|
|
498fe84b99 | ||
|
|
9b8b66ed83 | ||
|
|
9cc3dd4c92 | ||
|
|
bf23d81c9e | ||
|
|
f53100d8d3 | ||
|
|
acbe60cdf1 | ||
|
|
4c7f37e0f8 | ||
|
|
3ba696937c | ||
|
|
1647994471 | ||
|
|
674e09dc56 | ||
|
|
4c129d470f | ||
|
|
c630f01baa | ||
|
|
6018fc13b2 | ||
|
|
c4fc6cd0e2 | ||
|
|
eed8735238 | ||
|
|
8c0155e8bd | ||
|
|
b2d4412e59 | ||
|
|
4e3e45a88b | ||
|
|
af9b386d8a | ||
|
|
201b8a3bc6 | ||
|
|
6e1a994c26 | ||
|
|
2ccc05cb98 | ||
|
|
e7d22ffb11 | ||
|
|
4805c3615b | ||
|
|
44a5260050 | ||
|
|
d72d01823d | ||
|
|
1e166189ba | ||
|
|
66310523d1 | ||
|
|
87a041a212 | ||
|
|
9c52ec2751 | ||
|
|
9010269236 | ||
|
|
aac254d275 | ||
|
|
889b54f309 | ||
|
|
5d13ffda09 | ||
|
|
31b6d5971c | ||
|
|
304b6393d4 | ||
|
|
490f63a838 | ||
|
|
64d990ba10 | ||
|
|
cb019c405b | ||
|
|
26f80d25be | ||
|
|
fe9a1bb5fc | ||
|
|
79679f9b08 | ||
|
|
5ef5403d69 | ||
|
|
23afb3a129 | ||
|
|
fac08cf835 | ||
|
|
dd67cc7de0 | ||
|
|
8531534241 | ||
|
|
0993621bb8 | ||
|
|
45fe5c37cc | ||
|
|
35b60d8d89 | ||
|
|
63b4d37207 | ||
|
|
793268ed54 | ||
|
|
a4a05a5413 | ||
|
|
821979bab1 | ||
|
|
69be742619 | ||
|
|
298982b522 | ||
|
|
9f2b3d1cd4 | ||
|
|
800119fc92 | ||
|
|
725fc26880 | ||
|
|
ed27e87540 | ||
|
|
4fc2b5cf76 | ||
|
|
749d46930d | ||
|
|
7acafc26d6 | ||
|
|
2817936b87 | ||
|
|
022c5a8401 | ||
|
|
50fd944a62 | ||
|
|
9c9cd8164b | ||
|
|
8744449016 | ||
|
|
a23f66d889 | ||
|
|
a1596bb63c | ||
|
|
ada6fc18d9 | ||
|
|
19af145166 | ||
|
|
4ed81be0c6 | ||
|
|
1c62adcdb6 | ||
|
|
95f46930a5 | ||
|
|
50b0186c06 | ||
|
|
2c9d847a1d | ||
|
|
816cdda6bb | ||
|
|
76dd504589 | ||
|
|
938c69c816 | ||
|
|
d5fc402a89 | ||
|
|
065a7a1f1b | ||
|
|
833f97198e | ||
|
|
293d5cc02b | ||
|
|
dbb302f22a | ||
|
|
ccadffea73 | ||
|
|
b3390ce201 | ||
|
|
6039f6c691 | ||
|
|
c65cdc8699 | ||
|
|
7603886e04 | ||
|
|
406a96e4d1 | ||
|
|
87d21e7968 | ||
|
|
9dd79a8061 | ||
|
|
a1287416d4 | ||
|
|
00dc877f0a | ||
|
|
335081053d | ||
|
|
7d74d7d8f6 | ||
|
|
e98d8139bc | ||
|
|
4af22cfbc0 | ||
|
|
8a47aab01d | ||
|
|
395b208a0c | ||
|
|
ab4f8a0e39 | ||
|
|
7e110603b5 | ||
|
|
94455192ad | ||
|
|
c4ca24a537 | ||
|
|
ea1f5d1ff0 | ||
|
|
77b7355433 | ||
|
|
f6178c2f75 | ||
|
|
f1e04e5629 | ||
|
|
cc913a728a | ||
|
|
e727566741 | ||
|
|
0f68da4eb8 | ||
|
|
1a7abfd3b7 | ||
|
|
905c2775d2 | ||
|
|
126da9ee12 | ||
|
|
1080cb71c4 | ||
|
|
39a295901a | ||
|
|
7e55b0b099 | ||
|
|
f8c179b153 | ||
|
|
b4aa90b402 | ||
|
|
bea46fb879 | ||
|
|
379de05a61 | ||
|
|
a1ecf8a413 | ||
|
|
4d59d266c1 | ||
|
|
c2f222760a | ||
|
|
7e0a2ea4cb | ||
|
|
912808b672 | ||
|
|
a352d51711 | ||
|
|
4ad5ed64d7 | ||
|
|
3838a3b4fb | ||
|
|
7d748ae15e | ||
|
|
9aef3b7f1d | ||
|
|
9dab389b50 | ||
|
|
260b4adb8c | ||
|
|
c93bbbd302 | ||
|
|
009e5ce0c8 | ||
|
|
61bb4f53c0 | ||
|
|
182d92f9bd | ||
|
|
61a68a9e38 | ||
|
|
b6e33d70c8 | ||
|
|
7a26bf1a10 | ||
|
|
b6dd5e445e | ||
|
|
6f4b77a440 | ||
|
|
463584ea10 | ||
|
|
fd8d603831 | ||
|
|
a1ec83b002 | ||
|
|
041d3f2843 | ||
|
|
0b22127906 | ||
|
|
0b0b507827 | ||
|
|
df1a87eb4e | ||
|
|
16de289942 | ||
|
|
e203b1aa9a | ||
|
|
02dc97e413 | ||
|
|
f458114c5b | ||
|
|
8db09d2fed | ||
|
|
0ce7053598 | ||
|
|
6a96815c44 | ||
|
|
d7b0db41f4 | ||
|
|
9c9f3e7055 | ||
|
|
2f6d680b6c | ||
|
|
639f7939a3 | ||
|
|
95ce9cbf0c | ||
|
|
1e0ef941c7 | ||
|
|
7e5bd64885 | ||
|
|
de023bc6ea | ||
|
|
c7df6f50ca | ||
|
|
df71d251f0 | ||
|
|
0b9acc18cc | ||
|
|
58a7de3136 | ||
|
|
5bb77b1e03 | ||
|
|
e351322ce4 | ||
|
|
6b53ccc8bd | ||
|
|
33842b6f92 | ||
|
|
5e747b2cfd | ||
|
|
3f8de0efb9 | ||
|
|
63da3e4595 | ||
|
|
a04b69dc6a | ||
|
|
3be4764d54 | ||
|
|
2d4fec01b1 | ||
|
|
85c50eb542 | ||
|
|
ee62d00c96 | ||
|
|
52a32f0887 | ||
|
|
dd30be90ad | ||
|
|
4cb027eb73 | ||
|
|
8e0b8f2faf | ||
|
|
39491b78b1 | ||
|
|
7c82da8389 | ||
|
|
d5c13603a0 | ||
|
|
aad253bfad | ||
|
|
dac055e2ef | ||
|
|
85df5b9983 | ||
|
|
537cd42097 | ||
|
|
9d06fce5f9 | ||
|
|
c029e1005f | ||
|
|
b245a3d611 | ||
|
|
11d89c4268 | ||
|
|
6be84ab93e | ||
|
|
2f2c4d6740 | ||
|
|
703eae72eb | ||
|
|
6a629e963c | ||
|
|
06f4d0c117 | ||
|
|
b5e557ffb0 | ||
|
|
8bd6c4a335 | ||
|
|
29eaff9edc | ||
|
|
08afaf7fa5 | ||
|
|
45bab9fe71 | ||
|
|
514eb8f0e3 | ||
|
|
a7baa58c22 | ||
|
|
e548d8b1a5 | ||
|
|
92ff0fe624 | ||
|
|
a464f5a88e | ||
|
|
962a565d30 | ||
|
|
f242e60c1a | ||
|
|
7db50391f2 | ||
|
|
8b0d55a729 | ||
|
|
de6f341f41 | ||
|
|
c9fb530dbf | ||
|
|
836bcd09ec | ||
|
|
52a6836872 | ||
|
|
c00cadb37a | ||
|
|
b26c072f0d | ||
|
|
c99482f59d | ||
|
|
4aa0f567c3 | ||
|
|
4656021902 | ||
|
|
3df6144b77 | ||
|
|
6d335abe21 | ||
|
|
179b8a28b4 | ||
|
|
c620004168 | ||
|
|
893ff3e9d7 | ||
|
|
eec9a3cb8f | ||
|
|
d3c8042102 | ||
|
|
e590b44c77 | ||
|
|
e97bf479fb | ||
|
|
fe2f71c9b8 | ||
|
|
ee6c483dd4 | ||
|
|
e83ab86f1f | ||
|
|
ef237fc521 | ||
|
|
fd4d68038b | ||
|
|
8e458c21e9 | ||
|
|
4ab36cb3bc | ||
|
|
2d149f3a09 | ||
|
|
dfd2c7a3d2 | ||
|
|
6637762aec | ||
|
|
615eba3b40 | ||
|
|
dc183125fc | ||
|
|
c3b69d1201 | ||
|
|
3e1a50ab9a | ||
|
|
7565c71eb2 | ||
|
|
d743352bc0 | ||
|
|
75ca72e3a8 | ||
|
|
59d26c6825 | ||
|
|
5eed363e10 | ||
|
|
49b2fdbde9 | ||
|
|
623f6e9542 | ||
|
|
32356f23d3 | ||
|
|
321ddabfd5 | ||
|
|
bf93a6a1ab | ||
|
|
91aa5dc6dd | ||
|
|
56ed46b99a | ||
|
|
daaf21a4fe | ||
|
|
06a8f2afab | ||
|
|
cc04530c4a | ||
|
|
0b6f62977a | ||
|
|
2a2996c22b | ||
|
|
254aba0995 | ||
|
|
23a81b3121 | ||
|
|
c4659f816f | ||
|
|
fc13328e2a | ||
|
|
d1427e7d2e | ||
|
|
5584d1062e | ||
|
|
b3f28fac64 | ||
|
|
f65af7a308 | ||
|
|
559f50a3e3 | ||
|
|
18b809314c | ||
|
|
aa1f819c0d | ||
|
|
5a8cecf499 | ||
|
|
79f362abee | ||
|
|
42db74b522 | ||
|
|
8cb41fe5fb | ||
|
|
cd893fec15 | ||
|
|
3309e9f53f | ||
|
|
ae3a017143 | ||
|
|
2a01f178da | ||
|
|
a069d1e07d | ||
|
|
76fa7f5200 | ||
|
|
7a0a86d51c | ||
|
|
8e283e43db | ||
|
|
794744d5a3 | ||
|
|
5c3e69b045 | ||
|
|
9cbd595cfd | ||
|
|
a5074eca7d | ||
|
|
9386b117c1 | ||
|
|
7738248230 | ||
|
|
85f1f808d3 | ||
|
|
6dbafa22bb | ||
|
|
cadcd37681 | ||
|
|
a45ea6e1d8 | ||
|
|
187f24de81 | ||
|
|
8df1f48226 | ||
|
|
7dbf332a94 | ||
|
|
c09ddf6de1 | ||
|
|
9b5fa99d6a | ||
|
|
b9abf32007 | ||
|
|
09ecb80625 | ||
|
|
c4e9a0e340 | ||
|
|
b6cf4e1826 | ||
|
|
ecb1619399 | ||
|
|
83f4b7b699 | ||
|
|
9b81ddf4d5 | ||
|
|
4af17f0e40 | ||
|
|
2fbc036e69 | ||
|
|
d236a12b8f | ||
|
|
232be333ad | ||
|
|
192e5af797 | ||
|
|
deb39b8f34 | ||
|
|
197b41d97a | ||
|
|
7b11a57c6f | ||
|
|
eac7945fae | ||
|
|
1177d2263f | ||
|
|
b9478a1f65 | ||
|
|
025cf91679 | ||
|
|
442aa72ccc | ||
|
|
1c09f9c82d | ||
|
|
c6d2f633ea | ||
|
|
17bd60e3e9 | ||
|
|
1d24e82276 | ||
|
|
43d7a11ba8 | ||
|
|
02e12e17d6 | ||
|
|
e42d3d5620 | ||
|
|
5c83972401 | ||
|
|
b8df18481f | ||
|
|
1690dd4728 | ||
|
|
a86087ed7c | ||
|
|
745060f0b5 | ||
|
|
0a0a490a38 | ||
|
|
4f838685d6 | ||
|
|
6339ba09f0 | ||
|
|
f816a5b0da | ||
|
|
2f02e49716 | ||
|
|
a98a0b1ac2 | ||
|
|
034326c984 | ||
|
|
a965b05400 | ||
|
|
23c2e82c97 | ||
|
|
6c2de4a359 | ||
|
|
e9588279c8 | ||
|
|
7138d3518e | ||
|
|
f852325906 | ||
|
|
3c9082ece3 | ||
|
|
efe3a78499 | ||
|
|
98c77ffe18 | ||
|
|
31969a4939 | ||
|
|
b95d5f9f23 | ||
|
|
adf59d78d7 | ||
|
|
904ae3eab7 | ||
|
|
44d2309700 | ||
|
|
5dd6a3883a | ||
|
|
7a00c5ac71 | ||
|
|
23fbbb5191 | ||
|
|
a58b1ccfa5 | ||
|
|
5f0a3fe1c1 | ||
|
|
37b0a5c04c | ||
|
|
3fb4f60192 | ||
|
|
635fa84731 | ||
|
|
410bf4f0cd | ||
|
|
eebe115b78 | ||
|
|
70eb9f0678 | ||
|
|
b9de179fe8 | ||
|
|
ac29e24c1b | ||
|
|
d83572803d | ||
|
|
87faa4f108 | ||
|
|
ea3a0d64c1 | ||
|
|
8288d2f38b | ||
|
|
ff817dad0d | ||
|
|
ba554d5f45 | ||
|
|
90922a0ce0 | ||
|
|
a91cb546f9 | ||
|
|
3cb21d128f | ||
|
|
1b8d5d0648 | ||
|
|
03f035cdf0 | ||
|
|
ac95654409 | ||
|
|
ba33bb6aa4 | ||
|
|
2898c0507e | ||
|
|
d59edd6cca | ||
|
|
bc3079332c | ||
|
|
3c0395b19f | ||
|
|
9e7b591c78 | ||
|
|
b6107a1198 | ||
|
|
2d94018f12 | ||
|
|
42f0268829 | ||
|
|
74d568b1e7 | ||
|
|
0316fedd4c | ||
|
|
5247d93d7b | ||
|
|
bda5312917 | ||
|
|
e8c071304c | ||
|
|
4e155caae9 | ||
|
|
aedb7e44ba | ||
|
|
c58109c1b4 | ||
|
|
2167c7b6d4 | ||
|
|
22eef989f6 | ||
|
|
edf387363d | ||
|
|
14ffb7443f | ||
|
|
c21ba1e64c | ||
|
|
46b8069c6d | ||
|
|
2fb697f452 | ||
|
|
73f3293314 | ||
|
|
53fc25550b | ||
|
|
961d1775e5 | ||
|
|
bdaa359d11 | ||
|
|
a90cc018dd | ||
|
|
e06f784d35 | ||
|
|
5ce1b6a4c1 | ||
|
|
86d579a7ae | ||
|
|
d031ab5ceb | ||
|
|
dcf7230494 | ||
|
|
20aca45b39 | ||
|
|
4812c380c4 | ||
|
|
98ea43eca9 | ||
|
|
cd541c7a20 | ||
|
|
5a5cf31aba | ||
|
|
3074711828 | ||
|
|
aa97c928e9 | ||
|
|
5b4d6f2fee | ||
|
|
0e833c155a | ||
|
|
9ebb1b17d1 | ||
|
|
3397cf053c | ||
|
|
6e73b5934e | ||
|
|
7120ad5014 | ||
|
|
6286cfac28 | ||
|
|
1af09da2aa | ||
|
|
bea533acf8 | ||
|
|
5316c2479e | ||
|
|
9895f8c71e | ||
|
|
9749a398c0 | ||
|
|
2883a278ee | ||
|
|
6bbb9ec956 | ||
|
|
630556f7fe | ||
|
|
32a79c4fa1 | ||
|
|
eabfee6890 | ||
|
|
6b9b283149 | ||
|
|
a19ad6663c | ||
|
|
dfae97a8d7 | ||
|
|
93d03434eb | ||
|
|
76fcd341f6 | ||
|
|
cea53ddd1f | ||
|
|
cfc061c960 | ||
|
|
09bc1784e4 | ||
|
|
b723cf92e7 | ||
|
|
3d7b9f60da | ||
|
|
b049bdb33f | ||
|
|
a5c49d89d5 | ||
|
|
1f9a92e6ad | ||
|
|
6280611aea | ||
|
|
20c0e6e12b | ||
|
|
9aa6c2bd9f | ||
|
|
4450cce0e9 | ||
|
|
523cd421b3 | ||
|
|
88dbbe7bc3 | ||
|
|
cc63a633b5 | ||
|
|
72578e49dd | ||
|
|
83f1edb9da | ||
|
|
8016bd4e1f | ||
|
|
cb8b25da53 | ||
|
|
c517b98bae | ||
|
|
d66876a9c9 | ||
|
|
ad31df09fb | ||
|
|
1831e507fe | ||
|
|
b12a58632d | ||
|
|
8442373be9 | ||
|
|
0f7a9e5a74 | ||
|
|
74bce91db8 | ||
|
|
469b74ffb3 | ||
|
|
5dd0775737 | ||
|
|
33ed5402f4 | ||
|
|
aa655dadc4 | ||
|
|
0828025450 | ||
|
|
d9db3b695c | ||
|
|
c3e6199ecf | ||
|
|
926b9e0bfd | ||
|
|
05ba2ee2f2 | ||
|
|
cd865e0a0a | ||
|
|
08af876e01 | ||
|
|
a33fb05946 | ||
|
|
de52fa564b | ||
|
|
8e13d406d4 | ||
|
|
504f738cff | ||
|
|
fde7965bc9 | ||
|
|
d3ccc73796 | ||
|
|
a1154bf383 | ||
|
|
c9cf6d157b | ||
|
|
ef2f637d01 | ||
|
|
d130c73e77 | ||
|
|
3ecc389e66 | ||
|
|
3dbddfba41 | ||
|
|
e6cb497d1a | ||
|
|
703ac13185 | ||
|
|
a3e1c86953 | ||
|
|
3d64222655 | ||
|
|
5eb29fa99c | ||
|
|
84a24b76c7 | ||
|
|
89c2a64008 | ||
|
|
c1f992b11f | ||
|
|
ea60442fd6 | ||
|
|
d78c5fd7c2 | ||
|
|
3b98e4962a | ||
|
|
f739a1141d | ||
|
|
c498e9437e | ||
|
|
9f9a7c6b38 | ||
|
|
e3d2949c46 | ||
|
|
53f563ec08 | ||
|
|
d27c36c70c | ||
|
|
820834111b | ||
|
|
0c7b09c237 | ||
|
|
6eb8bb6241 | ||
|
|
ce0b18045b | ||
|
|
10d2d566a3 | ||
|
|
9c404185e8 | ||
|
|
3044c16ce1 | ||
|
|
729ef0190e | ||
|
|
3aab16f6db | ||
|
|
0682882ac1 | ||
|
|
da46cd78a3 | ||
|
|
f3cca3483d | ||
|
|
40dbb0ffaf | ||
|
|
53d3ac570e | ||
|
|
237022baae | ||
|
|
b11ef97b7d | ||
|
|
bccfd7bbbb | ||
|
|
634ed4a341 | ||
|
|
52aa87c145 | ||
|
|
597853cd6c | ||
|
|
1bd6568f94 | ||
|
|
4b2235aef2 | ||
|
|
faf1c26669 | ||
|
|
3aea5fb704 | ||
|
|
ea559592b5 | ||
|
|
c385c08e2f | ||
|
|
a74b925173 | ||
|
|
e84f71cd37 | ||
|
|
bc6428536c | ||
|
|
1c47a0e1a0 | ||
|
|
4d3e2f4fd3 | ||
|
|
a3430f7ce6 | ||
|
|
247b7ee1ab | ||
|
|
58eafce947 | ||
|
|
ed25cf9d23 | ||
|
|
8992c74d32 | ||
|
|
01f56c515b | ||
|
|
864d3a3b2f | ||
|
|
d68d2a4b18 | ||
|
|
d24da615b3 | ||
|
|
84c693ad22 | ||
|
|
747cbc1442 | ||
|
|
722cc54338 | ||
|
|
21abf0d215 | ||
|
|
83cb1b05d9 | ||
|
|
83d4c04646 | ||
|
|
0a7f601dec | ||
|
|
1d079c0879 | ||
|
|
3df4b5b28f | ||
|
|
14dc86e9ea | ||
|
|
b64d8bfab3 | ||
|
|
b41c8ad364 | ||
|
|
03c6709254 | ||
|
|
7a6fb46841 | ||
|
|
5b43407388 | ||
|
|
0e05301588 | ||
|
|
7a501164a9 | ||
|
|
b632984874 | ||
|
|
94407079bf | ||
|
|
57ab7ee6f2 | ||
|
|
62cf372d81 | ||
|
|
d3991ee672 | ||
|
|
b4603069bf | ||
|
|
60c522a696 | ||
|
|
99b0c8974b | ||
|
|
6ae2c8497f | ||
|
|
01b7509e61 | ||
|
|
194a09f21d | ||
|
|
3207365e3a | ||
|
|
b87b8ea931 | ||
|
|
58d21ddc15 | ||
|
|
80c1fee406 | ||
|
|
a595c1ceaf | ||
|
|
cc9c440614 | ||
|
|
37425713ba | ||
|
|
8e76d67bdf | ||
|
|
ab7e7ac12a | ||
|
|
99cb553cbd | ||
|
|
dcc396234c | ||
|
|
3788e8d7b3 | ||
|
|
715de1b18c | ||
|
|
7c2384cb94 | ||
|
|
5b1dd15749 | ||
|
|
041290e1c7 | ||
|
|
1f8417041d | ||
|
|
7ecd70a90b | ||
|
|
6d85ae8e8e | ||
|
|
f405877a16 | ||
|
|
d22c6f9d66 | ||
|
|
c87f0495c0 | ||
|
|
15fbf44948 | ||
|
|
10963bd808 | ||
|
|
0866c93573 | ||
|
|
48e475c9f6 | ||
|
|
2c5ab19374 | ||
|
|
9d50c31d2c | ||
|
|
b8b03c88e4 | ||
|
|
a65f57e0f5 | ||
|
|
491027b048 | ||
|
|
5e1971a951 | ||
|
|
1099e786df | ||
|
|
be95169c1b | ||
|
|
f64f672b20 | ||
|
|
7c0a5d60ef | ||
|
|
1f7bd9fd47 | ||
|
|
2a7c3eb2e6 | ||
|
|
32aeba2bb8 | ||
|
|
20e376c214 | ||
|
|
a0c1552726 | ||
|
|
46783e32d5 | ||
|
|
2eafac3ff6 | ||
|
|
9bd85b7023 | ||
|
|
eb9dcce30b | ||
|
|
a899cb48b3 | ||
|
|
ed8ac01f07 | ||
|
|
742ef58d89 | ||
|
|
c98bef5fc7 | ||
|
|
23044b3758 | ||
|
|
107ce78b12 | ||
|
|
5f81b8f0c1 | ||
|
|
83d75e704b | ||
|
|
a860e3eaf7 | ||
|
|
3d84bcc7ad | ||
|
|
c31504220d | ||
|
|
f8501cf7f4 | ||
|
|
81ef5a1dd2 | ||
|
|
29ec793386 | ||
|
|
187457df13 | ||
|
|
2eea214a42 | ||
|
|
639c2290b7 | ||
|
|
3044dcd3af | ||
|
|
3d64b16227 | ||
|
|
7bd22a5103 | ||
|
|
12cde3ddce | ||
|
|
860d70551f | ||
|
|
6db6fcc414 | ||
|
|
25c229de73 | ||
|
|
d954a54017 | ||
|
|
dcdd8cb3db | ||
|
|
e93c5f8265 | ||
|
|
77be4520dc | ||
|
|
cf76bcb2aa | ||
|
|
b272a61eac | ||
|
|
4ed5f278f8 | ||
|
|
6a16850960 | ||
|
|
78bbf1f04f | ||
|
|
52938202bd | ||
|
|
0eac8bb96e | ||
|
|
46ae1e759c | ||
|
|
e0c1308c2a | ||
|
|
0f29818030 | ||
|
|
dd39a25dd0 | ||
|
|
430dc04b09 | ||
|
|
39209f3bda | ||
|
|
0eaa43c05d | ||
|
|
6ed4be9135 | ||
|
|
02b6a21276 | ||
|
|
794ca573b8 | ||
|
|
5eadd36107 | ||
|
|
c6a370309d | ||
|
|
13e2514830 | ||
|
|
0b8be97fb5 | ||
|
|
c327dcb255 | ||
|
|
1b505cc0e6 | ||
|
|
032f771996 | ||
|
|
35d83d2306 | ||
|
|
dd23feb5ea | ||
|
|
e21d7bd000 | ||
|
|
d18cfa7b59 | ||
|
|
eca956cf80 | ||
|
|
587cc5acdd | ||
|
|
b5e9a4c191 | ||
|
|
3ced03ca7f | ||
|
|
9fa08fee99 | ||
|
|
3dfd7a9ab1 | ||
|
|
1713e5e2eb | ||
|
|
1d7342573b | ||
|
|
64fc295ecc | ||
|
|
12baabcc89 | ||
|
|
13839e9384 | ||
|
|
6b6dc8311a | ||
|
|
b1d8c3c1e3 | ||
|
|
d42a564de8 | ||
|
|
71d083a552 | ||
|
|
cf802c368d | ||
|
|
71037cb482 | ||
|
|
1f4e5cadd2 | ||
|
|
72e6e61970 | ||
|
|
4402b80ec9 | ||
|
|
3f0dbef7ea | ||
|
|
1ee8abf098 | ||
|
|
55eb1e2290 | ||
|
|
906990d9b5 | ||
|
|
518f3d2a8f | ||
|
|
6ba6ea0084 | ||
|
|
01c499d73f | ||
|
|
0de9cc82f6 | ||
|
|
7dba46df9b | ||
|
|
7621a71359 | ||
|
|
17f72f0ae2 | ||
|
|
47094ef046 | ||
|
|
6e28e79b29 | ||
|
|
463b258409 | ||
|
|
49d4c96ddf | ||
|
|
197d3e9d36 | ||
|
|
adcc368238 | ||
|
|
eff97c4d28 | ||
|
|
d2aeed7a0c | ||
|
|
9cb09feb65 | ||
|
|
fb084fa4cd | ||
|
|
846b8fb57e | ||
|
|
cb649830a0 | ||
|
|
bf4266c2e3 | ||
|
|
814a0f9016 | ||
|
|
098753520f | ||
|
|
43ae12f63b | ||
|
|
46937bbb87 | ||
|
|
5bf0c768f7 | ||
|
|
f124fd4c23 | ||
|
|
ff49d24743 | ||
|
|
99ca255aad | ||
|
|
d1a63b6fe0 | ||
|
|
3530a33fe7 | ||
|
|
d08efcfe3c | ||
|
|
337c0f2d0a | ||
|
|
1acf4a53d3 | ||
|
|
a2b0fb7d82 | ||
|
|
088c488e2d | ||
|
|
64ef934a3c | ||
|
|
39b690e96a | ||
|
|
08fc86698d | ||
|
|
89510f40d3 | ||
|
|
9f2207eb1f | ||
|
|
7bacf8c425 | ||
|
|
896672c4f4 | ||
|
|
a6552d7d54 | ||
|
|
cacef7b316 | ||
|
|
7a8642db94 | ||
|
|
2b8e0fdf06 | ||
|
|
dfb18b305d | ||
|
|
1606395546 | ||
|
|
abe4264a13 | ||
|
|
60d9bfb2b8 | ||
|
|
9141b3020f | ||
|
|
367c3c3e95 | ||
|
|
41a3903ac9 | ||
|
|
146d6ac538 | ||
|
|
fce93e16ae | ||
|
|
fb12fc03bf | ||
|
|
9c49b097c8 | ||
|
|
524e83fca4 | ||
|
|
f5999e84a2 | ||
|
|
ee2640bc43 | ||
|
|
2b93aa000c | ||
|
|
9c4b30b65c | ||
|
|
160dc5c470 | ||
|
|
bbe2896de2 | ||
|
|
673f641283 | ||
|
|
36d8d308f2 | ||
|
|
3482dd2a6b | ||
|
|
9cee8b7b78 | ||
|
|
4ae1b5e04d | ||
|
|
a34c8dc502 | ||
|
|
b42c1ddbbc | ||
|
|
9cc4860050 | ||
|
|
58921f7422 | ||
|
|
8dbd79d49b | ||
|
|
e2c67fa25a | ||
|
|
f332de8ba9 | ||
|
|
8dce9a6f66 | ||
|
|
1c616115b9 | ||
|
|
a97375552f | ||
|
|
1cf99d757a | ||
|
|
e84e94a8d1 | ||
|
|
cf85177c7f | ||
|
|
2b334774ce | ||
|
|
bc4a0f448b | ||
|
|
14ef86456f | ||
|
|
b083422321 | ||
|
|
12efc85baf | ||
|
|
68124dfdbe | ||
|
|
ee4158e90b | ||
|
|
0b3b7efccf | ||
|
|
b282d02a77 | ||
|
|
e07c813320 | ||
|
|
b76a00b6fe | ||
|
|
77e077d658 | ||
|
|
1cec48c065 | ||
|
|
30fa7f5a7f | ||
|
|
83edff6b26 | ||
|
|
fd58a5fe06 | ||
|
|
6064269936 | ||
|
|
d0929896cf | ||
|
|
990e87de1f | ||
|
|
8da47dcd00 | ||
|
|
15957bc9ac | ||
|
|
59f87bdffe | ||
|
|
ee31e93f98 | ||
|
|
d3f3d17536 | ||
|
|
88c1045180 | ||
|
|
2c06feb6a1 | ||
|
|
5b4fb961af | ||
|
|
3995ec0ff7 | ||
|
|
d2144905db | ||
|
|
b29c9c28fa | ||
|
|
aa2e68d1ad | ||
|
|
85b2ec93c8 | ||
|
|
c63d88d987 | ||
|
|
c21bde192b | ||
|
|
39f1f8aff5 | ||
|
|
3f8fcbf4e3 | ||
|
|
24cabf19af | ||
|
|
ae04f71e55 | ||
|
|
c56f0db891 | ||
|
|
ebf2765f2f | ||
|
|
8dac95d86d | ||
|
|
6125c0423f | ||
|
|
f3d1638131 | ||
|
|
8b699c58ae | ||
|
|
743a97c784 | ||
|
|
ea7ae5698a | ||
|
|
3ed9b3ec39 | ||
|
|
e8f3e3b88a | ||
|
|
82d16fbfde | ||
|
|
73c2e885e9 | ||
|
|
2e615e7570 | ||
|
|
d0f90546f3 | ||
|
|
22091cf272 | ||
|
|
f2f3fa4c9c | ||
|
|
0ab2ce9cf6 | ||
|
|
1b1424fe2f | ||
|
|
ee31c44ff1 | ||
|
|
e64af6b11b | ||
|
|
d6045931a5 | ||
|
|
9f99d5d42a | ||
|
|
327703871b | ||
|
|
b456f71b64 | ||
|
|
9576b8a8e9 | ||
|
|
e0037c7eaf | ||
|
|
59c5caa395 | ||
|
|
e4a35594ba | ||
|
|
5f44d1f2cf | ||
|
|
489c6f6730 | ||
|
|
c66afdf434 | ||
|
|
d62f5142cc | ||
|
|
5d4007918c | ||
|
|
5b49b5c535 | ||
|
|
7bd19e39a7 | ||
|
|
5037aed3ae | ||
|
|
c00bacb790 | ||
|
|
5672019203 | ||
|
|
7cb5da9971 | ||
|
|
62672bc4c8 | ||
|
|
705d62be18 | ||
|
|
d237522e8c | ||
|
|
44308fa026 | ||
|
|
766dcd0dd5 | ||
|
|
87302d6d86 | ||
|
|
aae19206a0 | ||
|
|
212c4c3a71 | ||
|
|
e1a443177e | ||
|
|
1210ea821a | ||
|
|
9721381383 | ||
|
|
5ea9a1d715 | ||
|
|
83f00d720e | ||
|
|
ee76fe584e | ||
|
|
063453b744 | ||
|
|
05b94d1d15 | ||
|
|
ed0c93aec3 | ||
|
|
3a0cb8b35c | ||
|
|
f0ed7f22db | ||
|
|
ac91f07d22 | ||
|
|
d5a7a691a2 | ||
|
|
229b23b350 | ||
|
|
18ee9518ec | ||
|
|
43e0328722 | ||
|
|
12cf93ddd8 | ||
|
|
03b4467173 | ||
|
|
45a5ac28f2 | ||
|
|
635eec013c | ||
|
|
679b9217e0 | ||
|
|
39f3571383 | ||
|
|
f027cc6a3e | ||
|
|
634ce6829a | ||
|
|
64b5f9af78 | ||
|
|
9269fcb8a7 | ||
|
|
571b34c990 | ||
|
|
f0d29cb755 | ||
|
|
897e0f1ba6 | ||
|
|
4cfa94d304 | ||
|
|
5e2abb8a33 | ||
|
|
0dc5052e36 | ||
|
|
774ad1e2ff | ||
|
|
52e91cc309 | ||
|
|
38fb72ece9 | ||
|
|
fcbefa218b | ||
|
|
c06b9d696e | ||
|
|
d3e6274939 | ||
|
|
ec904eb8a4 | ||
|
|
68c5e996d6 | ||
|
|
95a09c5463 | ||
|
|
125fb598d5 | ||
|
|
f8c13f939c | ||
|
|
e55e08339a | ||
|
|
ec86260eb5 | ||
|
|
adee5cec2b | ||
|
|
c846b24ebb | ||
|
|
85324d9109 | ||
|
|
10b410d46f | ||
|
|
c87fe7c265 | ||
|
|
ce0984573b | ||
|
|
b38c3a5035 | ||
|
|
15dca65bd1 | ||
|
|
e1aa43147d | ||
|
|
057b18e4be | ||
|
|
d4f2ced6a3 | ||
|
|
0b6c92fcd2 | ||
|
|
4491e71ee2 | ||
|
|
d09f78801c | ||
|
|
f26ea32897 | ||
|
|
6b7f6aee22 | ||
|
|
514fd0730c | ||
|
|
854596fb04 | ||
|
|
cf6b8f752b | ||
|
|
702f36be0d | ||
|
|
c738bc1e96 | ||
|
|
6c1c13ed11 | ||
|
|
d1b39cce2b | ||
|
|
5e67622374 | ||
|
|
90e95bbd76 | ||
|
|
6deaacadea | ||
|
|
2ae4003f7d | ||
|
|
dd951d5350 | ||
|
|
c86d0230ba | ||
|
|
79345691b8 | ||
|
|
2763cf37f7 | ||
|
|
66cfe050e0 | ||
|
|
e6b1595cde | ||
|
|
3b376a3d3d | ||
|
|
5ed04d97dd | ||
|
|
1e724af838 | ||
|
|
aec00821e5 | ||
|
|
bf1542c35b | ||
|
|
5a6d27cc19 | ||
|
|
2d83abbb85 | ||
|
|
a9afa42f52 | ||
|
|
a37f1c5dcb | ||
|
|
ecac2bd084 | ||
|
|
01aec3760c | ||
|
|
33cac4832f | ||
|
|
41b246903f | ||
|
|
4148e0c7a9 | ||
|
|
729bdd9c68 | ||
|
|
1f2d8d6340 | ||
|
|
de3cf327d4 | ||
|
|
fa3da880ea | ||
|
|
5dea57ddd5 | ||
|
|
caa59cbb62 | ||
|
|
51e32f72de | ||
|
|
32089aefce | ||
|
|
048bc53bd0 | ||
|
|
69e1d04638 | ||
|
|
de4c7a04c3 | ||
|
|
2c79181c05 | ||
|
|
5b37ec0de5 | ||
|
|
86dbea7ddb | ||
|
|
c49e7e181b | ||
|
|
b0dbbc51df | ||
|
|
91779a67fb | ||
|
|
1fe6cde934 | ||
|
|
3b8d00095e | ||
|
|
4221ddbb35 | ||
|
|
eeb0c22d90 | ||
|
|
eb801e66ae | ||
|
|
3fdf639bb4 | ||
|
|
4061ef65e4 | ||
|
|
0cf8af00b3 | ||
|
|
4d0d4ca6ea | ||
|
|
56cdae67c3 | ||
|
|
b50017cad4 | ||
|
|
62c616f197 | ||
|
|
fcfeda9f39 | ||
|
|
c85c38cde3 | ||
|
|
f606eda18d | ||
|
|
08c9e2dde3 | ||
|
|
dd965e03c5 | ||
|
|
3e3f5faacc | ||
|
|
7e41cd0d32 | ||
|
|
882bf7b020 | ||
|
|
da4e70bc60 | ||
|
|
f75f720c8a | ||
|
|
8d3aff5ff1 | ||
|
|
4e8c9078f7 | ||
|
|
41e4efcf1f | ||
|
|
f55e6138a5 | ||
|
|
147e24d835 | ||
|
|
4a94858ed1 | ||
|
|
e1d77e12b9 | ||
|
|
8e51d1eaf3 | ||
|
|
738835088b | ||
|
|
823b287eda | ||
|
|
a2c195e844 | ||
|
|
5a5503ab34 | ||
|
|
6e037a4d46 | ||
|
|
14597f74f4 | ||
|
|
fabf2479ec | ||
|
|
12c90bae04 | ||
|
|
37904db9cd | ||
|
|
499d9da75f | ||
|
|
198d612b87 | ||
|
|
e7667ba34b | ||
|
|
861ef7313b | ||
|
|
0291ffb951 | ||
|
|
93a7f9f3b8 | ||
|
|
62337e34a3 | ||
|
|
a24b70de75 | ||
|
|
6f7f10b2f7 | ||
|
|
bd7579fa65 | ||
|
|
430a2ea2f6 | ||
|
|
87569617ae | ||
|
|
b17beaccf1 | ||
|
|
cddba64151 | ||
|
|
ed3a18529a | ||
|
|
06bffb9cee | ||
|
|
a6e4ff83f0 | ||
|
|
a73025d20b | ||
|
|
0085ebb756 | ||
|
|
5d5f23cddc | ||
|
|
761c947e25 | ||
|
|
ec33766803 | ||
|
|
6daf891c5c | ||
|
|
9b04553fc8 | ||
|
|
a006bfeb24 | ||
|
|
71e1a8666d | ||
|
|
820c7e660a | ||
|
|
e94cb6a4b6 | ||
|
|
16e7872a82 | ||
|
|
aa4ebb07f8 | ||
|
|
4366d6a8c7 | ||
|
|
aa76d49234 | ||
|
|
d9df06644e | ||
|
|
554ee01263 | ||
|
|
b3055067d8 | ||
|
|
7f997d4b59 | ||
|
|
07717b2d08 | ||
|
|
7d2162b2d2 | ||
|
|
d15fa0cfb3 | ||
|
|
d2d959f455 | ||
|
|
a9e497f878 | ||
|
|
e423e9ffba | ||
|
|
3b8b52d222 | ||
|
|
b9514692fe | ||
|
|
73592f76ba | ||
|
|
47f0f1fab8 | ||
|
|
bb3a5620d3 | ||
|
|
268a1475f0 | ||
|
|
a46dad9fde | ||
|
|
3f7bf15cdb | ||
|
|
ed9f883301 | ||
|
|
2386d46609 | ||
|
|
0da1dbdc14 | ||
|
|
dba635aac5 | ||
|
|
e1ca49c082 | ||
|
|
a47d5f4f12 | ||
|
|
e809133c25 | ||
|
|
4e39979852 | ||
|
|
ff99fae928 | ||
|
|
2e0353c725 | ||
|
|
3c9983ca12 | ||
|
|
d3412e7de6 | ||
|
|
908fa55674 | ||
|
|
4a26332133 | ||
|
|
6c17c5a3a2 | ||
|
|
1e6f54f055 | ||
|
|
59da873adf | ||
|
|
fb4b1827db | ||
|
|
2e2263ed58 | ||
|
|
ac18199295 | ||
|
|
4a954853f2 | ||
|
|
1973f1a1de | ||
|
|
d36a448aff | ||
|
|
29aa481d2d | ||
|
|
5c276c34ea | ||
|
|
1d2b654021 | ||
|
|
bdfb0bb14f | ||
|
|
6eeb03f159 | ||
|
|
f806416b84 | ||
|
|
7e64e60625 | ||
|
|
a44d4bccc2 | ||
|
|
b8c3e95068 | ||
|
|
85e55f3be2 | ||
|
|
eaee9e0e49 | ||
|
|
e232c42fc5 | ||
|
|
c7c043c898 | ||
|
|
57b718ff32 | ||
|
|
f2775c18a1 | ||
|
|
d1001aea1a | ||
|
|
39e33f3538 | ||
|
|
a786fa534a | ||
|
|
2a0831ab5a | ||
|
|
c6b61b0838 | ||
|
|
e73e480939 | ||
|
|
e5d53b4964 | ||
|
|
1521fde0bd | ||
|
|
eee0e81e6b | ||
|
|
c7be3d73b9 | ||
|
|
8574d50eaa | ||
|
|
2841942899 | ||
|
|
eb33c9ec81 | ||
|
|
56679d2a8e | ||
|
|
415e79b523 | ||
|
|
9a6fdaa42b | ||
|
|
ff54168a10 | ||
|
|
b2acd5fa75 | ||
|
|
33d0f1fd94 | ||
|
|
bb4f09316a | ||
|
|
b5976dc2ff | ||
|
|
47824ec1ac | ||
|
|
9223b089b1 | ||
|
|
6da9a1285c | ||
|
|
3b90e24ff8 | ||
|
|
93da60c03b | ||
|
|
a3e501a355 | ||
|
|
0cdbe998cb | ||
|
|
35b0c49da5 | ||
|
|
d43c9f006b | ||
|
|
01a6ae61d2 | ||
|
|
a25d9e7aa7 | ||
|
|
440020726f | ||
|
|
36ad643a5f | ||
|
|
2b6800f01b | ||
|
|
b7cb3ee89f | ||
|
|
c9490b9ef8 | ||
|
|
87210a3c15 | ||
|
|
a314d33dea | ||
|
|
4c305b7532 | ||
|
|
19cd6a1bdf | ||
|
|
dbd276dd9c | ||
|
|
28913e3a72 | ||
|
|
d0465eccf1 | ||
|
|
3aedec3e2d | ||
|
|
f931d60ac1 | ||
|
|
4b4b233377 | ||
|
|
ccc9907034 | ||
|
|
7c8146128e | ||
|
|
6c506ba556 | ||
|
|
5a7baa0a3b | ||
|
|
26520abe2b | ||
|
|
1397c3248d | ||
|
|
c40dc39b88 | ||
|
|
a2753bb27d | ||
|
|
1c5529691b | ||
|
|
8e5bdc6b2b | ||
|
|
c5342e4a15 | ||
|
|
47a1cce5fa | ||
|
|
ef60fcb9ec | ||
|
|
b45a1bd913 | ||
|
|
9a8a0932d0 | ||
|
|
ccd06ee534 | ||
|
|
5f5e33027a | ||
|
|
3fbdfa1583 | ||
|
|
624bc1de56 | ||
|
|
597100d108 | ||
|
|
418f97dbba | ||
|
|
6a38671e3c | ||
|
|
4f8542c133 | ||
|
|
0f2d49b2d6 | ||
|
|
39324d6b55 | ||
|
|
b1ad187d1b | ||
|
|
606f15fec5 | ||
|
|
34a2f1ad65 | ||
|
|
ce20fb535d | ||
|
|
bfe8323753 | ||
|
|
f2b86ad3ed | ||
|
|
42b498654b | ||
|
|
888a4e1c0e | ||
|
|
09a0eb26d1 | ||
|
|
bf9e8048ff | ||
|
|
1c6d2864b6 | ||
|
|
21ebc226e4 | ||
|
|
8e3fef9096 | ||
|
|
829dccc1b6 | ||
|
|
a65caa62b3 | ||
|
|
ddb6f9c5b4 | ||
|
|
e152ab6cf2 | ||
|
|
4085af023a | ||
|
|
686e401368 | ||
|
|
8ec079945a | ||
|
|
4d8dbefc85 | ||
|
|
746c05081d | ||
|
|
2fdfa64b13 | ||
|
|
f406cfcebb | ||
|
|
80f35725e6 | ||
|
|
6741d99681 | ||
|
|
ad4ec14778 | ||
|
|
2bf16ad88b | ||
|
|
6c56581c15 | ||
|
|
ce89d9fbe0 | ||
|
|
9517191e35 | ||
|
|
e298f74310 | ||
|
|
c550c1373e | ||
|
|
6a1bb88c3b | ||
|
|
a585e96fdf | ||
|
|
e78ef493a1 | ||
|
|
c139378694 | ||
|
|
e51827bfcd | ||
|
|
d0eb3e760f | ||
|
|
9cfa3e5002 | ||
|
|
bb76621b7f | ||
|
|
c6746f6f77 | ||
|
|
714d453aa0 | ||
|
|
915c8d7afa | ||
|
|
1bf51a047e | ||
|
|
a7b2fe14b9 | ||
|
|
82278037ec | ||
|
|
0b592f86d0 | ||
|
|
ee4e003c2d | ||
|
|
b2904bc6fb | ||
|
|
67b94cf52e | ||
|
|
c308d2c9dd | ||
|
|
e36370b080 | ||
|
|
d0f005a6f1 | ||
|
|
a461fa9137 | ||
|
|
22e969fb59 | ||
|
|
34527c8395 | ||
|
|
012f4d68bc | ||
|
|
da8d0f3265 | ||
|
|
9b57d18143 | ||
|
|
9f0ac51e06 | ||
|
|
74b9e51cf7 | ||
|
|
ec4d8d7f9c | ||
|
|
011a19772d | ||
|
|
d8cef76592 | ||
|
|
856b0f4469 | ||
|
|
ca76d41f9a | ||
|
|
f7ba6e1121 | ||
|
|
df19dbc0de | ||
|
|
3b3e64e752 | ||
|
|
724283433a | ||
|
|
aea19b93b1 | ||
|
|
73020a711d | ||
|
|
7e62e671e3 | ||
|
|
2e845ac0ab | ||
|
|
2df41e52ed | ||
|
|
bf8e93f581 | ||
|
|
f5eec3283c | ||
|
|
223161c7d6 | ||
|
|
b8ba6e4827 | ||
|
|
15c8259ac2 | ||
|
|
720492932f | ||
|
|
a08a0fb084 | ||
|
|
827fbaac1f | ||
|
|
0de3175775 | ||
|
|
008b807803 | ||
|
|
08b53410b4 | ||
|
|
ce06b2cb18 | ||
|
|
d4d5a04487 | ||
|
|
297224bd31 | ||
|
|
9dc835c60c | ||
|
|
67a96bc2f3 | ||
|
|
92d98c8d59 | ||
|
|
7becb19da4 | ||
|
|
deee654426 | ||
|
|
552fd52bd5 | ||
|
|
f58fb3e556 | ||
|
|
ff5bf16111 | ||
|
|
8d22059462 | ||
|
|
91d144f5f7 | ||
|
|
c894b1d335 | ||
|
|
d30a7bf6a1 | ||
|
|
7f18cbf7d5 | ||
|
|
e7741b3d1f | ||
|
|
9d26cd787a | ||
|
|
561bc42259 | ||
|
|
649d5f56c9 | ||
|
|
3e4ca5ceb9 | ||
|
|
81b53112c1 | ||
|
|
fe1a635c14 | ||
|
|
521e6033a2 | ||
|
|
36b033c2f7 | ||
|
|
dc9bce915b | ||
|
|
37a0e733ad | ||
|
|
b3005eeec7 | ||
|
|
b21bfda7f6 | ||
|
|
a2f9662a99 | ||
|
|
274254b12e | ||
|
|
087e17511c | ||
|
|
139e868cce | ||
|
|
b6ec84e94d | ||
|
|
2b09392261 | ||
|
|
2132384736 | ||
|
|
8798f467e1 | ||
|
|
c9a7cfafd0 | ||
|
|
9d9585ecba | ||
|
|
3204bcb7a0 | ||
|
|
645a5206ec | ||
|
|
28baf4de5e | ||
|
|
04ddcd9489 | ||
|
|
a82f392cac | ||
|
|
15d308864c | ||
|
|
9af3a0d725 | ||
|
|
d2494fb97d | ||
|
|
c32ef61d3a | ||
|
|
6020d06038 | ||
|
|
b2b1b1cb47 | ||
|
|
023a605cad | ||
|
|
a61e31f646 | ||
|
|
0361436a66 | ||
|
|
c800670f39 | ||
|
|
7fa42cbdd3 | ||
|
|
f3f39e283e | ||
|
|
c9e1159c42 | ||
|
|
f9f8f7aebd | ||
|
|
ee3941b86a | ||
|
|
f9499e4b2f | ||
|
|
0ebd7e6a58 | ||
|
|
a100abc3b6 | ||
|
|
94f6945d5a | ||
|
|
870d57bfa5 | ||
|
|
a02f26f0eb | ||
|
|
442d771a7a | ||
|
|
242e11eefc | ||
|
|
97edd21784 | ||
|
|
2309656169 | ||
|
|
22ea322fc5 | ||
|
|
17f793a4bb | ||
|
|
2f16e17ed5 | ||
|
|
27ae79f50d | ||
|
|
eb18c0a505 | ||
|
|
103d1adc46 | ||
|
|
db661f124f | ||
|
|
4c3802878a | ||
|
|
23e560b638 | ||
|
|
b82a2ee735 | ||
|
|
7fbbae8cd4 | ||
|
|
83b6820458 | ||
|
|
b80d96d9c7 | ||
|
|
d8e69360b9 | ||
|
|
83ae9b66a3 | ||
|
|
3a7b4fd404 | ||
|
|
578557f3d7 | ||
|
|
92b7668067 | ||
|
|
95f9170736 | ||
|
|
52be2230cb | ||
|
|
8d2cecfd5c | ||
|
|
4738150c55 | ||
|
|
88b4847ba9 | ||
|
|
667fae9442 | ||
|
|
f532eb708e | ||
|
|
94a24b3f57 | ||
|
|
62fadc8200 | ||
|
|
1370625568 | ||
|
|
8eed440840 | ||
|
|
1aed2b9a43 | ||
|
|
6ec31cc7f9 | ||
|
|
6d3b3e6de2 | ||
|
|
10fd2b5b5a | ||
|
|
d020acca22 | ||
|
|
e704733743 | ||
|
|
79522194f1 | ||
|
|
aadc9e64d8 | ||
|
|
2eea258b9b | ||
|
|
796e019f12 | ||
|
|
036bd999fd | ||
|
|
1fdb5373c9 | ||
|
|
0ebe5ff55a | ||
|
|
ef610ab7de | ||
|
|
68454492e9 | ||
|
|
4593b652c4 | ||
|
|
2670915583 | ||
|
|
fb41535c69 | ||
|
|
b0890844e4 | ||
|
|
8239226208 | ||
|
|
49be1638ba | ||
|
|
e188047cfb | ||
|
|
afd01cc40c | ||
|
|
ee0b620d33 | ||
|
|
2d0c0d2aad | ||
|
|
e811236973 | ||
|
|
12078f07c6 | ||
|
|
c9ef3fa1d7 | ||
|
|
0a724e5a73 | ||
|
|
2c9e4aacf2 | ||
|
|
41486d44ee | ||
|
|
7fb326245e | ||
|
|
542645d876 | ||
|
|
9a5cf69251 | ||
|
|
b322633e8b | ||
|
|
487cd384a5 | ||
|
|
0b0055e0e2 | ||
|
|
57a9b313c4 | ||
|
|
81e8b0eb18 | ||
|
|
26da7d5e81 | ||
|
|
76b85ed554 | ||
|
|
d954568f7b | ||
|
|
92a2723815 | ||
|
|
4064fedf55 | ||
|
|
a27ad2e353 | ||
|
|
13aa3914ac | ||
|
|
20ba998383 | ||
|
|
612e718b91 | ||
|
|
cfc4fc354b | ||
|
|
61208817df | ||
|
|
bb58b5cbed | ||
|
|
eed8025ae2 | ||
|
|
a57d2c0e14 | ||
|
|
90afa5b3e1 | ||
|
|
a7c0cd405d | ||
|
|
bed22468ba | ||
|
|
93d4c3c47c | ||
|
|
0c190651cb | ||
|
|
3cf56ff7d5 | ||
|
|
f3f16c96e8 | ||
|
|
70a4ab863d | ||
|
|
d62ccfd234 | ||
|
|
370793a005 | ||
|
|
292c0356f1 | ||
|
|
4d6bcf27e9 | ||
|
|
130e417451 | ||
|
|
e6f2d9de86 | ||
|
|
415a1bffac | ||
|
|
798895d78d | ||
|
|
e24529d8d1 | ||
|
|
454436905c | ||
|
|
26feee1ed5 | ||
|
|
005b3356f2 | ||
|
|
4ac052c10b | ||
|
|
f1717f8319 | ||
|
|
cb000549bd | ||
|
|
c8d4d39068 | ||
|
|
8539298008 | ||
|
|
f7395b7d7c | ||
|
|
86758dcb70 | ||
|
|
f7c3b7c664 | ||
|
|
616d1a2c88 | ||
|
|
9f2b02d98e | ||
|
|
fc193b2d24 | ||
|
|
01d8d0dbec | ||
|
|
2712aa2ae2 | ||
|
|
7dcefa6bee | ||
|
|
70e3528809 | ||
|
|
c7780a2708 | ||
|
|
784d458e62 | ||
|
|
0e1e7875a8 | ||
|
|
03c6614aed | ||
|
|
26f33b9581 | ||
|
|
0ba66d4d47 | ||
|
|
5bc0b24913 | ||
|
|
da87fd7747 | ||
|
|
49b1608af8 | ||
|
|
8bc6d7af8c | ||
|
|
479690f011 | ||
|
|
2510f21c81 | ||
|
|
d6e55dffb0 | ||
|
|
7df0e22664 | ||
|
|
8939fe0b83 | ||
|
|
c4cbfd352e | ||
|
|
aea39080d6 | ||
|
|
658a4dbb2e | ||
|
|
607e16025d | ||
|
|
ecacdfaf49 | ||
|
|
d8719ede12 | ||
|
|
11834a4186 | ||
|
|
81381fac75 | ||
|
|
9fa91aab36 | ||
|
|
2897b66a1c | ||
|
|
ef77ac11ef | ||
|
|
382c857a4b | ||
|
|
c4bfbd3f94 | ||
|
|
f4c1a125e7 | ||
|
|
010e47634b | ||
|
|
086fda5f24 | ||
|
|
27b39c29b8 | ||
|
|
680ac9825a | ||
|
|
cfb3d14028 | ||
|
|
9f6bf62efc | ||
|
|
60ecceafec | ||
|
|
7b0b53a96d | ||
|
|
87d0e1d886 | ||
|
|
e84bc3754f | ||
|
|
26bab84d04 | ||
|
|
efea6136e7 | ||
|
|
c56026c18a | ||
|
|
375b76d46a | ||
|
|
1d9504d0f0 | ||
|
|
780b1f8acc | ||
|
|
b369fa2c7b | ||
|
|
7c96eb0819 | ||
|
|
febc3b7936 | ||
|
|
1e511dd655 | ||
|
|
71eafe436a | ||
|
|
66a5312908 | ||
|
|
14f4cdbbe5 | ||
|
|
e479e84bde | ||
|
|
19e3a2ecc4 | ||
|
|
d2290d509f | ||
|
|
434d4298df | ||
|
|
9b6c5741de | ||
|
|
e09a3b8cbf | ||
|
|
c3b9d3be8c | ||
|
|
0742e18edd | ||
|
|
cb59c2489b | ||
|
|
3158b544d5 | ||
|
|
e80270d1fa | ||
|
|
99b37f24bb | ||
|
|
fdbdcc4130 | ||
|
|
08104966b2 | ||
|
|
1c99133177 | ||
|
|
28780dc67e | ||
|
|
423ce54d8a | ||
|
|
73bab73db6 | ||
|
|
3e90471fa2 | ||
|
|
7f2db8a9a7 | ||
|
|
d5fc147ffd | ||
|
|
77ffd7bbf9 | ||
|
|
42775d3477 | ||
|
|
6932b8c378 | ||
|
|
fb9ac01533 | ||
|
|
cff641ef80 | ||
|
|
373abf1b24 | ||
|
|
f668596667 | ||
|
|
adf14f79e0 | ||
|
|
cd31799c4c | ||
|
|
93e0e815d7 | ||
|
|
05275c8938 | ||
|
|
19475db7dd | ||
|
|
63c005a3de | ||
|
|
4e984f5532 | ||
|
|
5cb79a4c1a | ||
|
|
083cb9cc9a | ||
|
|
f25903905c | ||
|
|
16216147bc | ||
|
|
8069e5568b | ||
|
|
0682df3801 | ||
|
|
b0800c7e38 | ||
|
|
e5c4391900 | ||
|
|
e33e7446ee | ||
|
|
f8c3e6adf3 | ||
|
|
de0eca7890 | ||
|
|
20ca17fcac | ||
|
|
6cbd1963bf | ||
|
|
2d292000a8 | ||
|
|
6e75f94505 | ||
|
|
e748bcb291 | ||
|
|
409a8c2a77 | ||
|
|
4cc09e49ac | ||
|
|
665b5363e3 | ||
|
|
5a05f40428 | ||
|
|
007b3644be | ||
|
|
93eba6ef68 | ||
|
|
0c7e593370 | ||
|
|
6b7df8fd50 | ||
|
|
783869b45c | ||
|
|
4919b9cb48 | ||
|
|
3f81e0ab82 | ||
|
|
96f2e150a1 | ||
|
|
a38af3a31c | ||
|
|
f29023f11d | ||
|
|
3a93600195 | ||
|
|
974373dc40 | ||
|
|
62de48e4ea | ||
|
|
1031af3dbc | ||
|
|
36aacba237 | ||
|
|
2530c577dc | ||
|
|
54b7920697 | ||
|
|
9ead37845e | ||
|
|
bff9141a4f | ||
|
|
f11b2f6782 | ||
|
|
b5054a7a7f | ||
|
|
bf61ed6310 | ||
|
|
e52756bf9f | ||
|
|
b0350d9412 | ||
|
|
824fcf1736 | ||
|
|
77d4895cf0 | ||
|
|
0440e6916b | ||
|
|
4c63a4046a | ||
|
|
624886bf5a | ||
|
|
545f90ba12 | ||
|
|
f97698858a | ||
|
|
424f99cea4 | ||
|
|
b13feea790 | ||
|
|
883b79f05d | ||
|
|
6dad4de8e3 | ||
|
|
daeff92065 | ||
|
|
56b3799a7a | ||
|
|
3244c57417 | ||
|
|
f1fcaccbaf | ||
|
|
4ef26f99fa | ||
|
|
909dca8f9a | ||
|
|
82fea3a989 | ||
|
|
caeb5effdc | ||
|
|
1e73b3d926 | ||
|
|
a0ad96acd3 | ||
|
|
b5b4ec0ed7 | ||
|
|
c1d61ddef6 | ||
|
|
c904c81d1c | ||
|
|
2c12c8e2b6 | ||
|
|
de88493acb | ||
|
|
421dbc28bd | ||
|
|
019bfb8a6b | ||
|
|
09f702879e | ||
|
|
c853c64a8f | ||
|
|
aeb5a3a261 | ||
|
|
6187fd2497 | ||
|
|
d58bf2f057 | ||
|
|
c035021cd6 | ||
|
|
561a5b2b2e | ||
|
|
2bfdd033eb | ||
|
|
938ae7dc8e | ||
|
|
3f7bd98174 | ||
|
|
dc67e8bbde | ||
|
|
d0cef987fe | ||
|
|
af4131bb4d | ||
|
|
d6e0638cd9 | ||
|
|
030b6f9720 | ||
|
|
d9634030d4 | ||
|
|
79f5502b8e | ||
|
|
179781dfee | ||
|
|
413fd61900 | ||
|
|
9f605cae29 | ||
|
|
c557279607 | ||
|
|
aa315d7c97 | ||
|
|
080b25e30c | ||
|
|
75920e0a09 | ||
|
|
f46ec05a3a | ||
|
|
a87969d85f | ||
|
|
3231ac15c9 | ||
|
|
627c75b6b9 | ||
|
|
823a845752 | ||
|
|
f28ac0c037 | ||
|
|
727e02b816 | ||
|
|
a7c8c08183 | ||
|
|
b872f90365 | ||
|
|
8c3446fb1f | ||
|
|
504222b564 | ||
|
|
108fcd5691 | ||
|
|
39724517cc | ||
|
|
90353bdd0f | ||
|
|
a42a251256 | ||
|
|
0e3dab081a | ||
|
|
e78dc4892f | ||
|
|
5cce1af3d7 | ||
|
|
92156e0520 | ||
|
|
9c13e48b62 | ||
|
|
bfc840d1a7 | ||
|
|
8bc7349635 | ||
|
|
ddbfc5c57a | ||
|
|
b1e97266e3 | ||
|
|
bb8c456a04 | ||
|
|
c5c9387226 | ||
|
|
ee6ff7daaa | ||
|
|
29c93d2b0d | ||
|
|
bb98627d2b | ||
|
|
3060866586 | ||
|
|
eacf121f78 | ||
|
|
c39535fdd0 | ||
|
|
c3311df2a8 | ||
|
|
99af52ac2f | ||
|
|
5a17314b2c | ||
|
|
05d974578d | ||
|
|
0d585a4d98 | ||
|
|
b968b4a9a4 | ||
|
|
07281f050c | ||
|
|
9c3757fb0c | ||
|
|
f5d5e59040 | ||
|
|
70880c066f | ||
|
|
5508c70f3a | ||
|
|
f100982b04 | ||
|
|
c8db2a85c6 | ||
|
|
3b086d130f | ||
|
|
cb8394fb07 | ||
|
|
a401f19ca1 | ||
|
|
444fd41d58 | ||
|
|
e3ccc6d558 | ||
|
|
8ed20e35ca | ||
|
|
bd16b485be | ||
|
|
9783de7464 | ||
|
|
9139fa7ec4 | ||
|
|
ba45539375 | ||
|
|
bb99abdebd | ||
|
|
89878dddf6 | ||
|
|
7118b70c1c | ||
|
|
a48539230c | ||
|
|
075ac9e280 | ||
|
|
d43f65366b | ||
|
|
052ab2d9bc | ||
|
|
74f7c93a6d | ||
|
|
36c3cac484 | ||
|
|
de5cd8e873 | ||
|
|
c9aa3bf62c | ||
|
|
2fc014af03 | ||
|
|
8abd010d08 | ||
|
|
c117ef3000 | ||
|
|
543407920d | ||
|
|
3dbdb541ed | ||
|
|
8f24858af8 | ||
|
|
f2667bfcae | ||
|
|
4bbafe13e6 | ||
|
|
f33bac5b59 | ||
|
|
85d2a33620 | ||
|
|
643f4fddf9 | ||
|
|
8ec8118d39 | ||
|
|
dcabf2738a | ||
|
|
a4fbe65259 | ||
|
|
83d76a0781 | ||
|
|
59fbc308b0 | ||
|
|
7266410840 | ||
|
|
e4312ed5cc | ||
|
|
1642a7714f | ||
|
|
d3d960112c | ||
|
|
c62be68bda | ||
|
|
a1befc78e0 | ||
|
|
eadc4788ce | ||
|
|
16d2084af2 | ||
|
|
6232e10d20 | ||
|
|
fdb8e0e2e5 | ||
|
|
7b803d855f | ||
|
|
3116fde51c | ||
|
|
714c542da9 | ||
|
|
68755757af | ||
|
|
1c7e272ac3 | ||
|
|
ba20766ec3 | ||
|
|
1e8bc50da5 | ||
|
|
258cbe7029 | ||
|
|
c9068f5820 | ||
|
|
31f66c7142 | ||
|
|
5e1d49cc7e | ||
|
|
495a72d167 | ||
|
|
e0eabb8d71 | ||
|
|
4c2c804d0d | ||
|
|
733c375afe | ||
|
|
5dce6e416e | ||
|
|
b54fe820cb | ||
|
|
201bf29429 | ||
|
|
b37c3d25a6 | ||
|
|
408417bbda | ||
|
|
3d8c8a36dd | ||
|
|
ca1b9d35dd | ||
|
|
2841bfe819 | ||
|
|
1694b10690 | ||
|
|
ec6df96a85 | ||
|
|
3dfd95b1dd | ||
|
|
5ba11f39d1 | ||
|
|
fa72a54c1a | ||
|
|
ef2ef300f0 | ||
|
|
ac20fe25e2 | ||
|
|
5a7ae77311 | ||
|
|
757ce4385b | ||
|
|
223f501c36 | ||
|
|
fd3bf981dc | ||
|
|
fa8473a224 | ||
|
|
c703429288 | ||
|
|
d0ff8d6175 | ||
|
|
51ee822794 | ||
|
|
6f6e66dacf | ||
|
|
163775df94 | ||
|
|
b9304d219b | ||
|
|
fd472403c1 | ||
|
|
a97d941835 | ||
|
|
7c56df2600 | ||
|
|
c38a63033a | ||
|
|
3f9e19a0fe | ||
|
|
b448df3843 | ||
|
|
2c927a34dd | ||
|
|
852310596b | ||
|
|
2d61ffa1b2 | ||
|
|
dfe4b66a43 | ||
|
|
ad657f6dda | ||
|
|
ce0e0b6038 | ||
|
|
7f0e840217 | ||
|
|
c35dd00923 | ||
|
|
6c9586a68d | ||
|
|
03bdf795b9 | ||
|
|
cd45a1976b | ||
|
|
a0bb84756c | ||
|
|
1af9dae3b9 | ||
|
|
f291f36f79 | ||
|
|
3e422f2538 | ||
|
|
0169174a87 | ||
|
|
fac7e1692c | ||
|
|
e457e1b316 | ||
|
|
c6cd0e4162 | ||
|
|
35a5e12840 | ||
|
|
241cd81cec | ||
|
|
85283222ee | ||
|
|
2e50fcb8c5 | ||
|
|
9817161260 | ||
|
|
d3b806c68d | ||
|
|
9a75bff2c1 | ||
|
|
b0917fbe87 | ||
|
|
ee417c9ff8 | ||
|
|
bbb33e6aa0 | ||
|
|
5d4981e7b7 | ||
|
|
66773e129b | ||
|
|
b5c363ba56 | ||
|
|
b8854771d8 | ||
|
|
56b59da0cf | ||
|
|
a5852b101b | ||
|
|
986bea25a6 | ||
|
|
d2413f03d9 | ||
|
|
d1dc1564f3 | ||
|
|
3ac5bf57fd | ||
|
|
1c983b781d | ||
|
|
b76357eb72 | ||
|
|
d31b6c527f | ||
|
|
c212028311 | ||
|
|
6d6b6e891b | ||
|
|
c71e77d13a | ||
|
|
4d63bddaef | ||
|
|
a27cc6ffa7 | ||
|
|
7e69be8c0d | ||
|
|
582b6f41a2 | ||
|
|
deb4855598 | ||
|
|
81a80c68fe | ||
|
|
8b03585d26 | ||
|
|
482b1c8f4c | ||
|
|
f5d19d43d4 | ||
|
|
d204f7635c | ||
|
|
cf42f33c69 | ||
|
|
bcc96cfec3 | ||
|
|
339831a1ed | ||
|
|
64bfbb0133 | ||
|
|
53ad952476 | ||
|
|
39364ee9a6 | ||
|
|
969e6501cb | ||
|
|
0a78217136 | ||
|
|
ed5e903eb4 | ||
|
|
4876ccb010 | ||
|
|
e20e50514a | ||
|
|
76b3a1c11f | ||
|
|
c0a90f78b8 | ||
|
|
b0f1f7156c | ||
|
|
d4ada06724 | ||
|
|
129512b82e | ||
|
|
16e236831d | ||
|
|
80c12b3e5f | ||
|
|
eb1e4982d5 | ||
|
|
7a4e76d720 | ||
|
|
9d42e8646a | ||
|
|
dcdd8c5a7f | ||
|
|
43561efb3b | ||
|
|
82ae977ba6 | ||
|
|
1f247704ff | ||
|
|
e076f6011d | ||
|
|
59dee1c499 | ||
|
|
4da57cbcb6 | ||
|
|
c3e130bdf0 | ||
|
|
974ae8a93e | ||
|
|
5afe38c130 | ||
|
|
174b9b50d6 | ||
|
|
674b02bda6 | ||
|
|
e8684ded7d | ||
|
|
003a3e355b | ||
|
|
e32f3de9b5 | ||
|
|
e7299b1125 | ||
|
|
8117b41e8a | ||
|
|
d3d39141be | ||
|
|
b7cbd36a07 | ||
|
|
4e75896240 | ||
|
|
72e5f36b67 | ||
|
|
f030d98dab | ||
|
|
0ac0f4dee7 | ||
|
|
fbecb17fea | ||
|
|
557c453b12 | ||
|
|
80038f6925 | ||
|
|
5136cfc7ce | ||
|
|
f55675ab2b | ||
|
|
0f284fd128 | ||
|
|
2ec352bc70 | ||
|
|
4f64412c19 | ||
|
|
7ab29c5516 | ||
|
|
aaf0aa12e1 | ||
|
|
7429e62d9d | ||
|
|
423fab8822 | ||
|
|
b6eb6642f8 | ||
|
|
36e790d6a9 | ||
|
|
7b64b8700c | ||
|
|
2cc9c06eab | ||
|
|
481e6bc498 | ||
|
|
8aa1fb33b6 | ||
|
|
2c992f1ece | ||
|
|
9ac919b7af | ||
|
|
755d87338d | ||
|
|
12fd86e681 | ||
|
|
d7b465359c | ||
|
|
63ecde0ea7 | ||
|
|
44bfa55bee | ||
|
|
520e1c4def | ||
|
|
6a63f97ce8 | ||
|
|
6195281e70 | ||
|
|
ad359e2659 | ||
|
|
a8a4f6fd2c | ||
|
|
0cb38676ef | ||
|
|
02e3ed4588 | ||
|
|
0b2aa433c5 | ||
|
|
41a992e42a | ||
|
|
5d45b04b22 | ||
|
|
3b0d8b37e7 | ||
|
|
f026c6238d | ||
|
|
d6cb0736d6 | ||
|
|
8488c657af | ||
|
|
e2b94620ad | ||
|
|
254dab3da7 | ||
|
|
42f090840c | ||
|
|
e5f40350d2 | ||
|
|
584736ad90 | ||
|
|
30f2092cc2 | ||
|
|
c52ad6fb7c | ||
|
|
06f6c37add | ||
|
|
00aaeb9305 | ||
|
|
ca9cb2f61b | ||
|
|
a8c72398ba | ||
|
|
01bd65f8e6 | ||
|
|
a0c7e5df57 | ||
|
|
9240a982d6 | ||
|
|
1d3d37034f | ||
|
|
913acabdd2 | ||
|
|
abbeff8126 | ||
|
|
ecfb439543 | ||
|
|
53f7b6c704 | ||
|
|
9623f44d83 | ||
|
|
fe9e0dd8c1 | ||
|
|
d5b351cd38 | ||
|
|
0106c10483 | ||
|
|
d6e742012e | ||
|
|
4dc4cfdbcf | ||
|
|
8220514307 | ||
|
|
1d06f7e93c | ||
|
|
ec965791bf | ||
|
|
00ea8e8984 | ||
|
|
9d5f2fb599 | ||
|
|
983a7fa707 | ||
|
|
5a5a243486 | ||
|
|
acb8b9de53 | ||
|
|
aca618080b | ||
|
|
b7bf83c88f | ||
|
|
36b90691c8 | ||
|
|
05e44d5148 | ||
|
|
67290157b7 | ||
|
|
64ed10530b | ||
|
|
eb9f6aca03 | ||
|
|
82f5ad4af2 | ||
|
|
ae87fd4a32 | ||
|
|
9c34adf2dc | ||
|
|
6812883511 | ||
|
|
50c5edf95e | ||
|
|
8ce4c1cdec | ||
|
|
f242e93b08 | ||
|
|
b544425667 | ||
|
|
8dbffe6fa7 | ||
|
|
8a00820dbb | ||
|
|
12f72ae5cc | ||
|
|
ef721bcd9d | ||
|
|
a122f992ba | ||
|
|
a6c6e221f7 | ||
|
|
7337a80550 | ||
|
|
5f094a894a | ||
|
|
4a70c470b5 | ||
|
|
8e7ed131b9 | ||
|
|
3a6dbaee94 | ||
|
|
1553d67d4b | ||
|
|
ea5df078a6 | ||
|
|
549f857aed | ||
|
|
4fb74b326f | ||
|
|
3dec991e7e | ||
|
|
f9c1e81fb3 | ||
|
|
999519bd94 | ||
|
|
fa81d7831b | ||
|
|
8c00857dd1 | ||
|
|
10d7487f2e | ||
|
|
26503f8ee3 | ||
|
|
879f19c70a | ||
|
|
a93b166c1f | ||
|
|
3917fa0dc5 | ||
|
|
caa983361e | ||
|
|
bdb4b4b6be | ||
|
|
728d96b1c6 | ||
|
|
edbe667e73 | ||
|
|
0466e19456 | ||
|
|
88843bf855 | ||
|
|
e11651648f | ||
|
|
174c37d75d | ||
|
|
a87b86b3e4 | ||
|
|
c9e0abfdf3 | ||
|
|
84f5948341 | ||
|
|
d8ffbe2c33 | ||
|
|
0e0a1c6432 |
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[**.std]
|
||||
insert_final_newline = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_size = 2
|
||||
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
test/assets
|
||||
test/reports
|
||||
test/sample
|
||||
test/tmp
|
||||
packages/bower-logger/test
|
||||
51
.eslintrc
Normal file
51
.eslintrc
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"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"],
|
||||
"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
|
||||
}
|
||||
}
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: bower
|
||||
44
.github/ISSUE_TEMPLATE.md
vendored
Normal file
44
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<!--
|
||||
|
||||
If you are reporting a new issue, make sure that we do not have any duplicates.
|
||||
You can ensure this by searching the issue list for this repository.
|
||||
|
||||
You are welcome to open issues to discuss important general topics concerning Bower.
|
||||
However for support questions, please consider using http://stackoverflow.com or
|
||||
asking for help in our Discord channel: https://discordapp.com/invite/0fFM7QF0KpZaDeN9
|
||||
|
||||
# BUG REPORT
|
||||
|
||||
Use the commands below to provide key information to reproduce:
|
||||
You do NOT have to include this information if this is a FEATURE REQUEST OR DISCUSSION
|
||||
|
||||
For more information about reporting bugs, see:
|
||||
https://github.com/bower/bower/wiki/Report-a-Bug
|
||||
|
||||
-->
|
||||
|
||||
**Output of `bower -v && npm -v && node -v`:**
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
**Additional environment details (proxy, private registry, etc.):**
|
||||
|
||||
|
||||
|
||||
**Steps to reproduce the issue:**
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Describe the results you received:**
|
||||
|
||||
|
||||
|
||||
**Describe the results you expected:**
|
||||
|
||||
|
||||
|
||||
**Additional information:**
|
||||
62
.github/workflows/nodejs.yml
vendored
Normal file
62
.github/workflows/nodejs.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Node v${{ matrix.node-version }} on ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# https://github.com/actions/setup-node/issues/27
|
||||
node-version: [0.10.x, 0.12.x, 4.x, 6.x, 8.x, 10.x, 12.x, 14.x]
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Set git config
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.symlinks true
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: install
|
||||
run: yarn && (cd packages/bower-json && yarn link) && yarn link bower-json
|
||||
- name: lint
|
||||
run: npm run lint
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: bower tests
|
||||
run: npm test
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-logger tests
|
||||
run: (cd packages/bower-logger && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-config tests
|
||||
run: (cd packages/bower-config && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-endpoint-parser tests
|
||||
run: (cd packages/bower-endpoint-parser && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-json tests
|
||||
run: (cd packages/bower-json && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-registry-client tests
|
||||
run: (cd packages/bower-registry-client && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,3 +1,15 @@
|
||||
node_modules
|
||||
components
|
||||
.DS_Store
|
||||
!lib/bin
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
|
||||
/test/assets/package-*/
|
||||
/test/assets/temp-*/
|
||||
/test/reports
|
||||
/test/tmp/
|
||||
|
||||
/bower.json
|
||||
/component.json
|
||||
/bower_components
|
||||
/test/sample
|
||||
!/test/sample/bower.json
|
||||
/npm-shrinkwrap.json
|
||||
|
||||
62
.jshintrc
62
.jshintrc
@@ -1,62 +0,0 @@
|
||||
{
|
||||
"predef": [
|
||||
"console",
|
||||
"describe",
|
||||
"it",
|
||||
"after",
|
||||
"afterEach",
|
||||
"before",
|
||||
"beforeEach"
|
||||
],
|
||||
|
||||
"indent": 2,
|
||||
"node": true,
|
||||
"devel": true,
|
||||
|
||||
"bitwise": false,
|
||||
"curly": false,
|
||||
"eqeqeq": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"latedef": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": true,
|
||||
"plusplus": true,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"quotmark": "single",
|
||||
"strict": false,
|
||||
"trailing": true,
|
||||
|
||||
"asi": false,
|
||||
"boss": false,
|
||||
"debug": false,
|
||||
"eqnull": true,
|
||||
"es5": false,
|
||||
"esnext": false,
|
||||
"evil": false,
|
||||
"expr": false,
|
||||
"funcscope": false,
|
||||
"globalstrict": false,
|
||||
"iterator": false,
|
||||
"lastsemic": false,
|
||||
"laxbreak": true,
|
||||
"laxcomma": false,
|
||||
"loopfunc": true,
|
||||
"multistr": false,
|
||||
"onecase": true,
|
||||
"regexdash": false,
|
||||
"scripturl": false,
|
||||
"smarttabs": false,
|
||||
"shadow": false,
|
||||
"sub": false,
|
||||
"supernew": true,
|
||||
"validthis": false,
|
||||
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"white": true
|
||||
}
|
||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
||||
**/node_modules/**
|
||||
**/test/assets/**
|
||||
**/bower_components/**
|
||||
test/sample
|
||||
@@ -1,3 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
649
CHANGELOG.md
649
CHANGELOG.md
@@ -1,22 +1,650 @@
|
||||
# Changelog
|
||||
|
||||
## Newer releases
|
||||
|
||||
Please see: https://github.com/bower/bower/releases
|
||||
|
||||
## 1.8.0 - 2016-11-07
|
||||
|
||||
- Download tar archives from GitHub when possible (#2263)
|
||||
- Change default shorthand resolver for github from `git://` to `https://`
|
||||
- Fix ssl handling by not setting GIT_SSL_NO_VERIFY=false (#2361)
|
||||
- Allow for removing components with url instead of name (#2368)
|
||||
- Show in warning message location of malformed bower.json (#2357)
|
||||
- Improve handling of non-semver versions in git resolver (#2316)
|
||||
- Fix handling of cached releases pluginResolverFactory (#2356)
|
||||
- Allow to type the entire version when conflict occured (#2243)
|
||||
- Allow `owner/reponame` shorthand for registering components (#2248)
|
||||
- Allow single-char repo names and package names (#2249)
|
||||
- Make `bower version` no longer honor `version` in bower.json (#2232)
|
||||
- Add `postinstall` hook (#2252)
|
||||
- Allow for `@` instead of `#` for `install` and `info` commands (#2322)
|
||||
- Upgrade all bundled modules
|
||||
|
||||
## 1.7.9 - 2016-04-05
|
||||
|
||||
- Show warnings for invalid bower.json fields
|
||||
- Update bower-json
|
||||
- Less strict validation on package name (allow spaces, slashes, and "@")
|
||||
|
||||
## 1.7.8 - 2016-04-04
|
||||
|
||||
- Don't ask for git credentials in non-interactive session, fixes #956 #1009
|
||||
- Prevent swallowing exceptions with programmatic api, fixes #2187
|
||||
- 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))
|
||||
- Allow for ignoring of child dependencies ([#1394](https://github.com/bower/bower/pull/1394))
|
||||
- Allow passing `--config.resolvers` through CLI ([#1922](https://github.com/bower/bower/pull/1922))
|
||||
- Use defaults values from package.json if it exists (bower init) ([#1731](https://github.com/bower/bower/issues/1731))
|
||||
- Properly use cerificates set in .bowerrc ([#1869](https://github.com/bower/bower/pull/1869))
|
||||
- Include package name when version conflict occurs ([#1917](https://github.com/bower/bower/pull/1917))
|
||||
- Add timeout for permission check ([yeoman/insight#35](https://github.com/yeoman/insight/pull/35))
|
||||
- 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
|
||||
|
||||
- Revert auto sorting of bower dependencies, fixes ([#1897](https://github.com/bower/bower/issues/1897))
|
||||
- Fix --save-exact feature for github endpoints, fixes ([#1925](https://github.com/bower/bower/issues/1925))
|
||||
- Fix `bower init` to support private flag again ([#1819](https://github.com/bower/bower/pull/1819))
|
||||
- Bump insight dependency to support prompt timeout ([#1102](https://github.com/bower/bower/issues/1102))
|
||||
|
||||
## 1.5.2 - 2015-08-25
|
||||
|
||||
- Revert update semver version from 2.x to 5.x, fixes ([#1896](https://github.com/bower/bower/issues/1896))
|
||||
- Make bower commands work from subdirectories, fixes ([#1893](https://github.com/bower/bower/issues/1893))
|
||||
- Put auto shallow cloning for git behind a flag, fixes ([#1764](https://github.com/bower/bower/issues/1764))
|
||||
|
||||
## 1.5.1 - 2015-08-24
|
||||
|
||||
- If cwd provided explicitly, force using it, fixes #1866
|
||||
|
||||
## 1.5.0 - 2015-08-24
|
||||
|
||||
- Pluggable Resolvers! http://bower.io/docs/pluggable-resolvers/
|
||||
- Update semver version from 2.x to 5.x ([#1852](https://github.com/bower/bower/issues/1852))
|
||||
- Auto-sort dependencies alphabetically ([#1381](https://github.com/bower/bower/issues/1381))
|
||||
- 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))
|
||||
- [fix] Update bower-registry-client so it uses the same bower-config as bower
|
||||
|
||||
## 1.4.0 - 2015-03-30
|
||||
|
||||
- Add login and unregister commands ([#1719](https://github.com/bower/bower/issues/1719))
|
||||
- Automatically detecting smart Git hosts ([#1628](https://github.com/bower/bower/issues/1628))
|
||||
- [bower/config#23] Allow npm config variables ([#1711](https://github.com/bower/bower/issues/1711))
|
||||
- [bower/config#24] Merge .bowerrc files upwards directory tree ([#1689](https://github.com/bower/bower/issues/1689))
|
||||
- Better homedir detection (514eb8f)
|
||||
- Add --save-exact flag ([#1654](https://github.com/bower/bower/issues/1654))
|
||||
- Ensure extracted files are readable (tar-fs) ([#1548](https://github.com/bower/bower/issues/1548))
|
||||
- The version command in the programmatic API now returns the new version ([#1755](https://github.com/bower/bower/issues/1755))
|
||||
- Some minor fixes: #1639, #1620, #1576, #1557, 962a565, a464f5a
|
||||
- Improved Windows support (AppVeyor CI, tests actually passing on Windows)
|
||||
- OSX testing enabled on TravisCI
|
||||
|
||||
It also includes improved test coverage (~60% -> ~85%) and many refactors.
|
||||
|
||||
## 1.3.12 - 2014-09-28
|
||||
|
||||
- [stability] Fix versions for unstable dependencies ([#1532](https://github.com/bower/bower/pull/1532))
|
||||
- [fix] Update tar-fs to support old tar format ([#1537](https://github.com/bower/bower/issues/1537))
|
||||
- [fix] Make analytics work again ([#1529](https://github.com/bower/bower/pull/1529))
|
||||
- [fix] Always disable analytics for non-interactive mode ([#1529](https://github.com/bower/bower/pull/1529))
|
||||
- [fix] Bower init can create private packages again ([#1522](https://github.com/bower/bower/issues/1522))
|
||||
- [fix] Show again missing newline for bower search output ([#1538](https://github.com/bower/bower/issues/1538))
|
||||
|
||||
## 1.3.11 - 2014-09-17
|
||||
|
||||
- [fix] Restore install missing dependencies on update ([1519](https://github.com/bower/bower/pull/1519))
|
||||
|
||||
## 1.3.10 - 2014-09-13
|
||||
|
||||
- [fix] Back down concurrency from 50 to 5 ([#1483](https://github.com/bower/bower/pull/1483))
|
||||
- [fix] Read .bowerrc from specified cwd ([#1301](https://github.com/bower/bower/pull/1301))
|
||||
- [fix] Disable shallow clones except those from GitHub ([#1393](https://github.com/bower/bower/pull/1393))
|
||||
- [fix] Expose bower version ([#1478](https://github.com/bower/bower/pull/1478))
|
||||
- [fix] Bump dependencies, including "request" ([#1467](https://github.com/bower/bower/pull/1467))
|
||||
- [fix] Prevent an error when piping bower output to head ([#1508](https://github.com/bower/bower/pull/1508))
|
||||
- [fix] Disable removing unnecessary resolutions ([#1061](https://github.com/bower/bower/pull/1061))
|
||||
- [fix] Display the output of hooks again ([#1484](https://github.com/bower/bower/issues/1484))
|
||||
- [fix] analytics: true in .bowerrc prevents user prompt ([#1470](https://github.com/bower/bower/pull/1470))
|
||||
- [perf] Use `tar-fs` instead of `tar` for faster TAR extraction ([#1490](https://github.com/bower/bower/pull/1490))
|
||||
|
||||
## 1.3.9 - 2014-08-06
|
||||
|
||||
- [fix] Handle `tmp` sometimes returning an array ([#1434](https://github.com/bower/bower/pull/1434))
|
||||
|
||||
## 1.3.8 - 2014-7-11
|
||||
|
||||
- [fix] Lock down `tmp` package dep ([#1403](https://github.com/bower/bower/pull/1403), [#1407](https://github.com/bower/bower/pull/1407))
|
||||
|
||||
## 1.3.7 - 2014-07-04
|
||||
|
||||
- [fix] callstack error when processing installed packages with circular dependencies ([#1349](https://github.com/bower/bower/issues/1349))
|
||||
- [fix] Prevent bower list --paths` failing with TypeError ([#1383](https://github.com/bower/bower/issues/1383))
|
||||
- "bower install" fails if there's no bower.json in current directory ([#922](https://github.com/bower/bower/issues/922))
|
||||
|
||||
## 1.3.6 - 2014-07-02
|
||||
|
||||
- [fix] Make --force always re-run installation ([#931](https://github.com/bower/bower/issues/931))
|
||||
- [fix] Disable caching for local resources ([#1356](https://github.com/bower/bower/issues/1356))
|
||||
- [fix] Emit errors instead throwing them when using bower.commands API ([#1297](https://github.com/bower/bower/issues/1297))
|
||||
- [fix] Main files and bower.json are never ignored ([#547](https://github.com/bower/bower/issues/547))
|
||||
- [fix] Check if pkgMeta is undefined during uninstall command ([#1329](https://github.com/bower/bower/issues/1329))
|
||||
- [fix] Make custom tmp dir and ignores play well with each other ([#1299](https://github.com/bower/bower/issues/1299))
|
||||
- Warn users when installing package with missing properties ([#694](https://github.com/bower/bower/issues/694))
|
||||
|
||||
|
||||
## 1.3.5 - 2014-06-06
|
||||
- Search compatible versions in fetching packages ([#1147](https://github.com/bower/bower/issues/1147))
|
||||
|
||||
## 1.3.4 - 2014-06-02
|
||||
|
||||
- Resolve a situation in which the install process gets into an infinite loop ([#1169](https://github.com/bower/bower/issues/1169))
|
||||
- Improved CLI output for conflicts ([#1284](https://github.com/bower/bower/issues/1284))
|
||||
- Changed `bower version` to mirror the tag format of `npm version` ([#1278](https://github.com/bower/bower/issues/1278))
|
||||
- Allow short commit SHAs to be used ([#990](https://github.com/bower/bower/issues/990))
|
||||
|
||||
## 1.3.3 - 2014-04-24
|
||||
|
||||
- Do not cache moving targets like branches ([#1242](https://github.com/bower/bower/issues/1242))
|
||||
- Suppress output if --quiet option is specified ([#1124](https://github.com/bower/bower/pull/1124))
|
||||
- Use "svn export" for efficiency ([#1224](https://github.com/bower/bower/pull/1224))
|
||||
- Prevent loading insights and analytics on CI ([#1221](https://github.com/bower/bower/issues/1221))
|
||||
- Make "bower list" respect custom components directory ([#1237](https://github.com/bower/bower/issues/1237))
|
||||
- Improve non-interactive loading performance 2x ([#1238](https://github.com/bower/bower/issues/1238))
|
||||
- Load commands only on demand, improving performance ([#1232](https://github.com/bower/bower/pull/1232))
|
||||
|
||||
## 1.3.2 - 2014-04-05
|
||||
|
||||
- Added yui moduleType [PR #1129](https://github.com/bower/bower/pull/1129)
|
||||
- Fixes for concurrency issues [PR #1211](https://github.com/bower/bower/pull/1211)
|
||||
- `link` now installs package dependencies [PR #891](https://github.com/bower/bower/pull/891)
|
||||
- Improved conflict installation message [Commit](https://github.com/bower/bower/commit/bea533acf87903d4b411bfbaa7df93f852ef46a3)
|
||||
- Add --production switch to "prune" command [PR #1168](https://github.com/bower/bower/pull/1168)
|
||||
|
||||
|
||||
## 1.3.1 - 2014-03-10
|
||||
|
||||
- No longer ask for permission to gather analytics when running on in a CI environment.
|
||||
|
||||
|
||||
## 1.3.0 - 2014-03-10
|
||||
|
||||
- **Removed support for node 0.8.** It may still work but we will no longer fix bugs for older versions of node.
|
||||
- Add **Bower Insight** for opt-in analytics integration to help improve tool and gain insight on community trends
|
||||
- Old overview of [Insight](https://github.com/yeoman/yeoman/wiki/Insight), [Issue #260](https://github.com/bower/bower/issues/260)
|
||||
- Reporting to GA. Public Dashboard is in progress.
|
||||
- [Turn off interactive mode](https://github.com/bower/bower/issues/1162) if you run Bower in a CI environment
|
||||
- Add `moduleType` property to bower init ([#934](https://github.com/bower/bower/pull/934))
|
||||
- Fix prune command to log only after cleanup is completed ([#1023](https://github.com/bower/bower/issues/1023))
|
||||
- Fix git resolver to ignore pre-release versions ([#1017](https://github.com/bower/bower/issues/1017))
|
||||
- Fix shorthand flag for `save` option on `uninstall` command ([#1031](https://github.com/bower/bower/pull/1031))
|
||||
- Add `bower version` command ([#961](https://github.com/bower/bower/pull/961))
|
||||
- Add .bowerrc option to use `--save` by default when using `bower install` command ([#1074](https://github.com/bower/bower/pull/1074))
|
||||
- Fix git resolver caching ([#1083](https://github.com/bower/bower/issues/1083))
|
||||
- Fix reading versions from cache directory ([#1076](https://github.com/bower/bower/pull/1076))
|
||||
- Add svn support ([#1055](https://github.com/bower/bower/pull/1055))
|
||||
- Allow circular dependencies to be installed ([#1104](https://github.com/bower/bower/pull/1104))
|
||||
- Add scripts/hooks support ([#718](https://github.com/bower/bower/pull/718))
|
||||
|
||||
_NOTE_: It's advisable that users use `--config.interactive=false` on automated scripts.
|
||||
|
||||
|
||||
## 1.2.8 - 2013-12-02
|
||||
- Fix absolute paths ending with / not going through the FsResolver, ([#898](https://github.com/bower/bower/issues/898))
|
||||
- Allow query string parameters in package URLs
|
||||
- Swapped 'unzip' module for 'decompress-zip', and some other small unzipping fixes([#873](https://github.com/bower/bower/issues/873), [#896](https://github.com/bower/bower/issues/896))
|
||||
- Allow the root-check to be overridden when calling bower programmatically.
|
||||
- Fixed some bugs relating to packages with a very large dependency tree
|
||||
- Fix a bug caused by a recent change to semver
|
||||
|
||||
|
||||
## 1.2.7 - 2013-09-29
|
||||
|
||||
- Do not swallow sync errors when using the programmatic API ([#849](https://github.com/bower/bower/issues/849))
|
||||
- Fix resolutions not being saved if `--force-latest` is specified ([#861](https://github.com/bower/bower/issues/861))
|
||||
- Fix `bower register` warning about URL conversion, even if no conversion occurred
|
||||
- Fix `bower update` not correctly catching up branch commits
|
||||
- Add configured directory in `.bowerrc` to the ignores in `bower init` ([#854](https://github.com/bower/bower/issues/854))
|
||||
- Fix some case sensitive issues with data stored in registry cache (e.g.: jquery/jQuery, [#859](https://github.com/bower/bower/issues/859))
|
||||
- Fix bower not checking out a tag if it looks like a semver (e.g.: 1.0, [#872](https://github.com/bower/bower/issues/872))
|
||||
- Fix install & update commands printing the wrong versions in some cases ([#879](https://github.com/bower/bower/issues/879))
|
||||
- Give priority to mime type headers when deciding if a package need to be extracted, except if it is `octet-stream`
|
||||
|
||||
_NOTE_: It's advisable that users run `bower cache clean`.
|
||||
|
||||
|
||||
## 1.2.6 - 2013-09-04
|
||||
|
||||
- Bower now reports download progress even for servers that do not respond with `content-length` header.
|
||||
- Do not translate endpoints when registering a package to a private registry server ([#832](https://github.com/bower/bower/issues/832))
|
||||
- Detect corrupted downloads by comparing downloaded bytes with `content-length` header if possible; this fixes Bower silently failing on unstable networks ([#824](https://github.com/bower/bower/issues/824) and [#792](https://github.com/bower/bower/issues/792))
|
||||
- Fix quotes in fields causing Bower to crash in the `init` command ([#841](https://github.com/bower/bower/issues/841))
|
||||
|
||||
|
||||
## 1.2.5 - 2013-08-28
|
||||
|
||||
- Fix persistent conflict resolutions not working correctly for branches ([#818](https://github.com/bower/bower/issues/818))
|
||||
- Fix Bower failing to run if HOME is not set ([#826](https://github.com/bower/bower/issues/826))
|
||||
- Bower now prints a warning if HOME is not set ([#827](https://github.com/bower/bower/issues/827))
|
||||
- Fix progress message being fired after completion of long running `git clone` commands
|
||||
- Other minor improvements
|
||||
|
||||
|
||||
## 1.2.4 - 2013-08-23
|
||||
|
||||
- Fix ignored nested folders not being correctly handled in some cases ([#814](https://github.com/bower/bower/issues/814))
|
||||
|
||||
|
||||
## 1.2.3 - 2013-08-22
|
||||
|
||||
- Fix read of environment variables that map to config properties with dashes and also support nested ones ([#8@bower-config](https://github.com/bower/config/issues/8))
|
||||
- Fix `bower info <package> <property>` printing the available versions (it shouldn't!)
|
||||
- Fix interactive shell not being correctly detected in node `0.8.x` ([#802](https://github.com/bower/bower/issues/802))
|
||||
- Fix `extraneous` flag in the `list` command being incorrectly set for saved dev dependencies in some cases
|
||||
- Fix linked dependencies not being read in `bower list` on Windows ([#813](https://github.com/bower/bower/issues/813))
|
||||
- Fix update notice not working with `--json`
|
||||
|
||||
|
||||
## 1.2.2 - 2013-08-20
|
||||
|
||||
- Standardize prompt behaviour with and without `--json`
|
||||
- Improve detection of `git` servers that do not support shallow clones ([#805](https://github.com/bower/bower/issues/805))
|
||||
- Ignore remote tags (tags ending with ^{})
|
||||
- Fix bower not saving the correct endpoint in some edge cases ([#806](https://github.com/bower/bower/issues/806))
|
||||
|
||||
|
||||
## 1.2.1 - 2013-08-19
|
||||
|
||||
- Fix bower throwing on non-semver targets ([#800](https://github.com/bower/bower/issues/800))
|
||||
|
||||
|
||||
## 1.2.0 - 2013-08-19
|
||||
|
||||
- __Bower no longer installs a pre-release version by default, that is, if no version/range is specified__ ([#782](https://github.com/bower/bower/issues/782))
|
||||
- __`bower info <package>` will now show the latest `<package>` information along with the available versions__ ([#759](https://github.com/bower/bower/issues/759))
|
||||
- __`bower link` no longer requires an elevated user on Windows in most cases__ ([#472](https://github.com/bower/bower/issues/472))
|
||||
- __Init command now prompts for the whole `bower.json` spec properties, filling in default values for `author` and `homepage` based on `git` settings__ ([#693](https://github.com/bower/bower/issues/693))
|
||||
- Changes to endpoint sources in `bower.json` are now catched up by `bower install` and `bower update` ([#788](https://github.com/bower/bower/issues/788))
|
||||
- Allow semver ranges in `bower cache clean`, e.g. `bower cache clean jquery#<2.0.0` ([#688](https://github.com/bower/bower/issues/688))
|
||||
- Normalize `bower list --paths` on Windows ([#279](https://github.com/bower/bower/issues/279))
|
||||
- Multiple mains are now correctly outputted as an array in `bower list --paths` ([#784](https://github.com/bower/bower/issues/784))
|
||||
- Add `--relative` option to `bower list --json` so that Bower outputs relative paths instead of absolute ([#714](https://github.com/bower/bower/issues/714))
|
||||
- `bower list --paths` now outputs relative paths by default; can be turned off with `--no-relative` ([#785](https://github.com/bower/bower/issues/785))
|
||||
- Bower no longer fails if `symlinks` to files are present in the `bower_components` folder ([#783](https://github.com/bower/bower/issues/783) and [#791](https://github.com/bower/bower/issues/791))
|
||||
- Disable git templates/hooks when running `git` ([#761](https://github.com/bower/bower/issues/761))
|
||||
- Add instructions to setup git workaround for proxies when execution of `git` fails ([#250](https://github.com/bower/bower/issues/250))
|
||||
- Ignore `component.json` if it looks like a component(1) file ([#556](https://github.com/bower/bower/issues/556))
|
||||
- Fix multi-user usage on bower when it creates temporary directories to hold some files
|
||||
- Fix prompting causing an invalid JSON output when running commands with `--json`
|
||||
- When running Bower commands programmatically, prompting is now disabled by default (see the updated programmatic [usage](https://github.com/bower/bower#programmatic-api) for more info)
|
||||
- Other minor improvements and fixes
|
||||
|
||||
Fix for `#788` requires installed components to be re-installed.
|
||||
|
||||
|
||||
## 1.1.2 - 2013-08-10
|
||||
|
||||
- Detect and fallback if the git server does not support `--depth=1` when cloning ([#747](https://github.com/bower/bower/issues/747))
|
||||
|
||||
|
||||
## 1.1.1 - 2013-08-08
|
||||
|
||||
- Fix silent fail when spawning child processes in some edge cases ([#722](https://github.com/bower/bower/issues/722))
|
||||
- Fix `home` command not guessing the correct URL for `GitHub` ssh endpoints (requires `bower cache-clean`)
|
||||
- Fix bower not correctly filtering packages with symlinks in some cases ([#730](https://github.com/bower/bower/issues/730))
|
||||
- Fix multi-user usage on bower when it falls back to create a `/tmp/bower` folder ([#743](https://github.com/bower/bower/issues/743))
|
||||
- Bower now sends a fake user agent when behind a proxy by default, so that corporate proxies do not block requests ([#698](https://github.com/bower/bower/issues/698))
|
||||
- Bower now translates GitHub public `git://` URLs to `git@` when behind a proxy ([#731](https://github.com/bower/bower/issues/731))
|
||||
- Minor improvements to the CLI output on small terminals
|
||||
- Minor programmatic usage improvements
|
||||
- Minor help usage fixes
|
||||
|
||||
|
||||
## 1.1.0 - 2013-08-03
|
||||
|
||||
- __Fix `--save` and `--save-dev` not working correctly for the uninstall command in some situations__
|
||||
- __Attempting to register a package that declares `"private": true` in `bower.json` will result in an error ([#162](https://github.com/bower/bower/issues/162))__
|
||||
- __Fix retry strategy on download error that was causing some strange I/O errors__ ([#699](https://github.com/bower/bower/issues/699) and [#704](https://github.com/bower/bower/issues/704))
|
||||
- __`bower prune` now clears pruned packages dependencies if they are also extraneous__ ([#708](https://github.com/bower/bower/issues/708))
|
||||
- __`bower uninstall` now uninstalls uninstalled packages dependencies if they are not shared ([#609](https://github.com/bower/bower/issues/609))__
|
||||
- Fix `bower list` display the `incompatible` label even if they are compatible ([#710](https://github.com/bower/bower/issues/710))
|
||||
- Fix `bower cache clean` not working correctly when `package#non-semver` is specified
|
||||
- Implement no operation `completion` command to prevent weird output when hitting tab ([#691](https://github.com/bower/bower/issues/691))
|
||||
- Fix `bower info --help` ([#703](https://github.com/bower/bower/issues/703))
|
||||
- Add colorized output for `bower info <package>#<version>` ([#571](https://github.com/bower/bower/issues/571))
|
||||
- Added `bower ls` as an alias to `bower list`
|
||||
- Fix regression: do not create a json file when saving is required, warn instead
|
||||
- Ignore linked packages when reading dependencies in `bower init` ([#709](https://github.com/bower/bower/issues/709))
|
||||
- `bower list` is now able to (partially) reconstruct the dependency tree, even for dependencies not declared in `bower.json` ([#622](https://github.com/bower/bower/issues/622))
|
||||
|
||||
|
||||
## 1.0.3 - 2013-07-30
|
||||
|
||||
- Fix some changes not being saved to bower.json ([#685](https://github.com/bower/bower/issues/685))
|
||||
- Fix `bower info <package> <property>` not showing information related to property of the latest version of that package ([#684](https://github.com/bower/bower/issues/684))
|
||||
|
||||
|
||||
## 1.0.2 - 2013-07-30
|
||||
|
||||
- Fix severe bug originated from a wrong merge that caused conflict messages to not show up correctly
|
||||
|
||||
|
||||
## 1.0.1 - 2013-07-29
|
||||
|
||||
- Fix `bower register` going ahead even if the answer was `no` ([#644](https://github.com/bower/bower/issues/644))
|
||||
- Fix local endpoints with backslashes on Windows ([#2@endpoint-parser](https://github.com/bower/endpoint-parser/pull/2))
|
||||
- Fix usage of multiple registries in the registry-client ([#3@registry-client](https://github.com/bower/registry-client/pull/3) and [#2@registry-client](https://github.com/bower/registry-client/pull/2))
|
||||
- File extensions now have more priority than mime types when deciding if extraction is necessary ([#657](https://github.com/bower/bower/pull/657))
|
||||
- Fix `Bower` not working when calling `.bat`/`.cmd` commands on Windows; it affected people using `Git portable` ([#626](https://github.com/bower/bower/issues/626))
|
||||
- Fix `bower list --paths` not resolving all files to absolute paths when the `main` property contained multiple files ([660](https://github.com/bower/bower/issues/660))
|
||||
- Fix `Bower` renaming `bower.json` and `component.json` files to `index.json` when it was the only file in the folder ([#674](https://github.com/bower/bower/issues/674))
|
||||
- Ignore symlinks when copying/extracting since they are not portable, specially across different hard-drives ([#665](https://github.com/bower/bower/issues/665))
|
||||
- Local file/dir endpoints are now exclusively referenced by an absolute path or relative path starting with `.` ([#666](https://github.com/bower/bower/issues/666))
|
||||
- Linked packages `bower.json` files are now parsed, making `bower list` account linked packages dependencies ([#659](https://github.com/bower/bower/issues/659))
|
||||
- Bower now fails to run with sudo unless `--allow-root` is passed ([#498](https://github.com/bower/bower/issues/498))
|
||||
- Add additional system information such as node version, bower version, OS version when an error occurs ([#670](https://github.com/bower/bower/issues/670))
|
||||
- `bower install` no longer overwrites `linked` packages unless it needs to ([#593](https://github.com/bower/bower/issues/593)).
|
||||
- All endpoint parts are now trimmed so that the Manager can better detect similar endpoints ([#3@endpoint-parser](https://github.com/bower/endpoint-parser/pull/3))
|
||||
- `bower register` now shows the server that will be used ([#647](https://github.com/bower/endpoint-parser/pull/647))
|
||||
|
||||
|
||||
## 1.0.0 - 2013-07-23
|
||||
|
||||
Total rewrite of bower.
|
||||
The list bellow highlights the most important stuff.
|
||||
For a complete list of changes that this rewrite and release brings please read: https://github.com/bower/bower/wiki/Rewrite-state
|
||||
|
||||
|
||||
- Clear architecture and separation of concerns
|
||||
- Much much faster
|
||||
- `--json` output for all commands
|
||||
- `--offline` usage for all commands, except `register`
|
||||
- Proper `install` and `update` commands, similar to `npm` in behaviour
|
||||
- Named endpoints when installing, e.g. `bower install backbone-amd=backbone#~1.0.0`
|
||||
- New interactive conflict resolution strategy
|
||||
- Prevent human errors when using `register`
|
||||
- New `home` command, similar to `npm`
|
||||
- New `cache list` command
|
||||
- New `prune` command
|
||||
- Many many general bug fixes
|
||||
|
||||
Non-backwards compatible changes:
|
||||
|
||||
- The value of the `json` property from .bowerrc is no longer used
|
||||
- `--map` and `--sources` from the list command were removed, use `--json` instead
|
||||
- Programmatic usage changed, specially the commands interface
|
||||
|
||||
Users upgrading from `bower-canary` and `bower@~0.x.x` should do a `bower cache clean`.
|
||||
Additionally you may remove the `~/.bower` folder manually since it's no longer used.
|
||||
On Windows the folder is located in `AppData/bower`.
|
||||
|
||||
|
||||
## 0.10.0 - 2013-07-02
|
||||
|
||||
- __Allow specific commits to be targeted__ ([#275](https://github.com/bower/bower/issues/275))
|
||||
- __Change bower default folder from `components` to `bower_components`__ ([#434](https://github.com/bower/bower/issues/434))
|
||||
- __Support semver pre-releases and builds__ ([#188](https://github.com/bower/bower/issues/188))
|
||||
- Use `Content-Type` and `Content-Disposition` to guess file types, such as zip files ([#454](https://github.com/bower/bower/pull/454))
|
||||
- Fix bower failing silently when using an invalid version value in the bower.json file ([#439](https://github.com/bower/bower/issues/439))
|
||||
- Fix bower slowness when downloading after redirects ([#437](https://github.com/bower/bower/issues/437))
|
||||
- Detect and error out with a friendly message when `git` is not installed ([#362](https://github.com/bower/bower/issues/362))
|
||||
- Add `--quiet` and `--silent` CLI options ([#343](https://github.com/bower/bower/issues/343))
|
||||
- Minor programmatic usage improvements
|
||||
|
||||
_NOTE_: The `components` folder will still be used if already created, making it easier for users to upgrade.
|
||||
|
||||
## 0.9.2 - 2013-04-28
|
||||
- Better fix for [#429](https://github.com/bower/bower/issues/429)
|
||||
|
||||
## 0.9.1 - 2013-04-27
|
||||
- Update `package.json`, docs and other stuff to point to the new `Bower` organisation on GitHub
|
||||
- Fix root label of `bower list` being an absolute path; now uses the package name
|
||||
- Fix `bower update <pkg>` updating all packages; now throws when updating an unknown package
|
||||
- Fix `list` command when package use different names than the `guessed` one ([#429](https://github.com/bower/bower/issues/429))
|
||||
|
||||
## 0.9.0 - 2013-04-25
|
||||
- __Change from `component.json` to `bower.json`__ ([#39](https://github.com/bower/bower/issues/39))
|
||||
- __Compatibility with `node 0.10.x`, including fix hangs/errors when extracting `zip` files__
|
||||
- Fix `--save` and `--save-dev` not working with URLs that get redirected ([#417](https://github.com/bower/bower/issues/417))
|
||||
- Fix `init` command targeting `~commit` instead of `*`. ([#385](https://github.com/bower/bower/issues/385))
|
||||
- Remove temporary directories before exiting ([#345](https://github.com/bower/bower/issues/345))
|
||||
- Integrate `update-notifier` ([#202](https://github.com/bower/bower/issues/202))
|
||||
- Use `json` name when a package name was inferred ([#192](https://github.com/bower/bower/issues/192))
|
||||
- Fix `bin/bower` not exiting with an exit code greater than zero when an error occurs ([#187](https://github.com/bower/bower/issues/187))
|
||||
- Fix `--save` and `--save-dev` saving resolved shorthands instead of the actual shorthands
|
||||
- Fix bower using user defined git templates ([#324](https://github.com/bower/bower/issues/324))
|
||||
- Add command abbreviations ([#262](https://github.com/bower/bower/issues/262))
|
||||
- Improve help messages and fix abuse of colors in output
|
||||
- Wait for every package to resolve before printing error messages ([#290](https://github.com/bower/bower/issues/290))
|
||||
- Add `shorthand_resolver` to allow shorthands to be resolved to repositories other than GitHub ([#278](https://github.com/bower/bower/issues/278))
|
||||
|
||||
## 0.8.6 - 2013-04-03
|
||||
- Emergency fix for `node 0.8.x` users to make `zip` extraction work again
|
||||
|
||||
## 0.8.5 - 2013-03-04
|
||||
- Fix `cache-clean` command clearing the completion cache when the command was called with specific packages
|
||||
- Add error message when an error is caught parsing an invalid `component.json`
|
||||
|
||||
## 0.8.4 - 2013-03-01
|
||||
- Fix some more duplicate async callbacks being called twice
|
||||
- Preserve new lines when saving `component.json` ([#285](https://github.com/bower/bower/issues/285))
|
||||
|
||||
## 0.8.3 - 2013-02-27
|
||||
- Fix error when using the `update` command ([#282](https://github.com/bower/bower/issues/282))
|
||||
|
||||
## 0.8.2 - 2013-02-26
|
||||
- Fix some errors in windows while removing directories, had to downgrade `rimraf` ([#274](https://github.com/bower/bower/issues/274))
|
||||
- Prevent duplicate package names in error summaries ([#277](https://github.com/bower/bower/issues/277))
|
||||
|
||||
## 0.8.1 - 2013-02-25
|
||||
- Fix some async callbacks being fired twice ([#274](https://github.com/bower/bower/issues/274))
|
||||
|
||||
## 0.8.0 - 2013-02-24
|
||||
- __Add init command similar to `npm init`__ ([#219](https://github.com/bower/bower/issues/219))
|
||||
- __Add devDependencies__ support ([#251](https://github.com/bower/bower/issues/251))
|
||||
- __Add `--save-dev` flag to install/uninstall commands__ ([#258](https://github.com/bower/bower/issues/258))
|
||||
- `cache-clean` command now clears links pointing to nonexistent folders ([#182](https://github.com/bower/bower/issues/182))
|
||||
- Fix issue when downloading assets behind a proxy using `https` ([#230](https://github.com/bower/bower/issues/230))
|
||||
- Fix --save saving unresolved components ([#240](https://github.com/bower/bower/issues/240))
|
||||
- Fix issue when extracting some zip files ([#225](https://github.com/bower/bower/issues/225))
|
||||
- Fix automatic conflict resolver not selecting the correct version
|
||||
- Add `--sources` option to the `list` command ([#235](https://github.com/bower/bower/issues/235))
|
||||
- Automatically clear cache when git commands fail with code 128 ([#216](https://github.com/bower/bower/issues/216))
|
||||
- Fix `bower` not working correctly behind a proxy in some commands ([#208](https://github.com/bower/bower/issues/208))
|
||||
|
||||
## 0.7.1 - 2013-02-20
|
||||
- Remove postinstall script from `bower` installation
|
||||
|
||||
## 0.7.0 - 2013-02-01
|
||||
- __Ability to resolve conflicts__ ([#214](https://github.com/bower/bower/issues/214))
|
||||
- __Ability to search and publish to different endpoints by specifying them in the `.bowerrc` file__
|
||||
- __Experimental autocompletion__
|
||||
- __Ability to exclude (ignore) files__
|
||||
- Fix minor issues in the cache clean command
|
||||
- Better error message for invalid semver tags ([#185](https://github.com/bower/bower/issues/185))
|
||||
- Only show discover message in the list command only if there are packages
|
||||
- Fix mismatch issue due to reading cached component.json files ([#214](https://github.com/bower/bower/issues/214))
|
||||
- Better error messages when reading invalid .bowerrc files ([#220](https://github.com/bower/bower/issues/220))
|
||||
- Fix update command when used in packages pointing to assets ([#197](https://github.com/bower/bower/issues/197))
|
||||
- Bower now obeys packages's `.bowerrc` if they define a different `json` ([#205](https://github.com/bower/bower/issues/205))
|
||||
|
||||
## 0.6.8 - 2012-12-14
|
||||
- Improve list command
|
||||
- Does not fetch versions if not necessary (for --map and --paths options)
|
||||
- Add --offline option to prevent versions from being fetched
|
||||
- Fix uninstall command not firing the `end` event
|
||||
- Fix error when executing an unknown command ([#179](https://github.com/bower/bower/issues/179))
|
||||
- Fix help for the ls command (alias of list)
|
||||
|
||||
## 0.6.7 - 2012-12-10
|
||||
- Fix uninstall removing all unsaved dependencies ([#178](https://github.com/bower/bower/issues/178))
|
||||
- Fix uninstall --force flag in some cases
|
||||
- Add --silent option to the register option, to avoid questioning
|
||||
- Fix possible issues with options in some commands
|
||||
- Fix error reporting when reading invalid project component.json
|
||||
|
||||
## 0.6.6 - 2012-12-03
|
||||
- Improve error handling while reading component.json
|
||||
- Fix package name not being correctly collected in the error summary
|
||||
|
||||
## 0.6.5 - 2012-12-01
|
||||
- Fix error summary not being displayed in some edge cases
|
||||
- Fix bower not fetching latest commits correctly in some cases
|
||||
|
||||
## 0.6.4 - 2012-11-29
|
||||
- Fix permission on downloaded files ([#160](https://github.com/twitter/bower/issues/160))
|
||||
- Fix permission on downloaded files ([#160](https://github.com/bower/bower/issues/160))
|
||||
|
||||
## 0.6.3 - 2012-11-24
|
||||
- Fix version not being correctly set for local packages ([#155](https://github.com/twitter/bower/issues/155))
|
||||
- Fix version not being correctly set for local packages ([#155](https://github.com/bower/bower/issues/155))
|
||||
|
||||
## 0.6.2 - 2012-11-23
|
||||
- Fix uninstall --save when there is no component.json
|
||||
|
||||
## 0.6.1 - 2012-11-22
|
||||
- Fix uninstall when the project component.json has no deps saved ([#153](https://github.com/twitter/bower/issues/153))
|
||||
- Fix uncaught errors when using file writter (they are now caught and reported)
|
||||
- Fix temporary directories not being deleted when an exception occurs ([#153](https://github.com/twitter/bower/issues/140))
|
||||
- Fix uninstall when the project component.json has no deps saved ([#153](https://github.com/bower/bower/issues/153))
|
||||
- Fix uncaught errors when using file writer (they are now caught and reported)
|
||||
- Fix temporary directories not being deleted when an exception occurs ([#153](https://github.com/bower/bower/issues/140))
|
||||
|
||||
## 0.6.0 - 2012-11-21
|
||||
- __Add link command__ (similar to npm)
|
||||
@@ -30,18 +658,17 @@
|
||||
- Add windows instructions to the README
|
||||
|
||||
## 0.5.0 - 2012-11-19
|
||||
|
||||
- __Remove package.json support__
|
||||
- __Support for local path repositories__ ([#132](https://github.com/twitter/bower/issues/132))
|
||||
- __Support for local path repositories__ ([#132](https://github.com/bower/bower/issues/132))
|
||||
- `install --save` now saves the correct tag (e.g: ~0.0.1) instead of 'latest'
|
||||
- `install --save` now saves packages pointing directly to assets correctly
|
||||
- Bower automatically creates a component.json when install with `--save` is used
|
||||
- Fix issues with list command ([#142](https://github.com/twitter/bower/issues/142))
|
||||
- Fix local paths not being saved when installing with --save ([#114](https://github.com/twitter/bower/issues/114))
|
||||
- `uninstall` now uninstalls nested dependencies if they are not shared ([#83](https://github.com/twitter/bower/issues/83))
|
||||
- Fix issues with list command ([#142](https://github.com/bower/bower/issues/142))
|
||||
- Fix local paths not being saved when installing with --save ([#114](https://github.com/bower/bower/issues/114))
|
||||
- `uninstall` now uninstalls nested dependencies if they are not shared ([#83](https://github.com/bower/bower/issues/83))
|
||||
- `uninstall` now warns when a dependency conflict occurs and aborts.
|
||||
It will only proceed if the `--force` flag is passed
|
||||
- Bower now detects mismatches between the version specified in the component.json and the tag, informing the user
|
||||
- `bower ls` now informs when a package has a new commit (for non-tagged repos)
|
||||
- Add jshintrc and fix a lot of issues related with JSHint warnings
|
||||
- `bower register` now prompts if the user really wants to proceed
|
||||
- `bower register` now prompts if the user really wants to proceed
|
||||
|
||||
167
CONTRIBUTING.md
Normal file
167
CONTRIBUTING.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# 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. 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))
|
||||
* Comment on issues and drive to resolution
|
||||
|
||||
## High-impact Involvement
|
||||
|
||||
* 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)
|
||||
|
||||
## Team Meetings
|
||||
|
||||
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
|
||||
|
||||
The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
[features requests](#features) and [submitting pull
|
||||
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),
|
||||
[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="features"></a>
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature. Please
|
||||
provide as much detail and context as possible.
|
||||
|
||||
|
||||
<a name="pull-requests"></a>
|
||||
## Pull requests
|
||||
|
||||
Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code), otherwise you risk spending a lot of
|
||||
time working on something that the project's developers might not want to merge
|
||||
into the project.
|
||||
|
||||
Please adhere to the coding conventions used throughout a project (indentation,
|
||||
accurate comments, etc.) and any other requirements (such as test coverage).
|
||||
|
||||
Adhering to the following this process is the best way to get your work
|
||||
included in the project:
|
||||
|
||||
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
|
||||
and configure the remotes:
|
||||
|
||||
```bash
|
||||
# Clone your fork of the repo into the current directory
|
||||
git clone https://github.com/<your-username>/bower
|
||||
# Navigate to the newly cloned directory
|
||||
cd bower
|
||||
# Assign the original repo to a remote called "upstream"
|
||||
git remote add upstream https://github.com/bower/bower
|
||||
```
|
||||
|
||||
2. If you cloned a while ago, get the latest changes from upstream:
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
```
|
||||
|
||||
3. Create a new topic branch (off the main project development branch) to
|
||||
contain your feature, change, or fix:
|
||||
|
||||
```bash
|
||||
git checkout -b <topic-branch-name>
|
||||
```
|
||||
|
||||
4. Make sure to update, or add to the tests when appropriate. Patches and
|
||||
features will not be accepted without tests. Run `npm test` to check that
|
||||
all tests pass after you've made changes.
|
||||
|
||||
5. Commit your changes in logical chunks. Please adhere to these [git commit
|
||||
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||
or your code is unlikely be merged into the main project. Use Git's
|
||||
[interactive rebase](https://help.github.com/articles/interactive-rebase)
|
||||
feature to tidy up your commits before making them public.
|
||||
|
||||
6. Locally merge (or rebase) the upstream development branch into your topic branch:
|
||||
|
||||
```bash
|
||||
git pull [--rebase] upstream master
|
||||
```
|
||||
|
||||
7. Push your topic branch up to your fork:
|
||||
|
||||
```bash
|
||||
git push origin <topic-branch-name>
|
||||
```
|
||||
|
||||
8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
|
||||
with a clear title and description.
|
||||
|
||||
9. If you are asked to amend your changes before they can be merged in, please
|
||||
use `git commit --amend` (or rebasing for multi-commit Pull Requests) and
|
||||
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.
|
||||
|
||||
|
||||
<a name="maintainers"></a>
|
||||
## Maintainers
|
||||
|
||||
If you have commit access, please follow this process for merging patches and cutting new releases.
|
||||
|
||||
### Reviewing changes
|
||||
|
||||
1. Check that a change is within the scope and philosophy of the project.
|
||||
2. Check that a change has any necessary tests and a proper, descriptive commit message.
|
||||
3. Checkout the change and test it locally.
|
||||
4. If the change is good, and authored by someone who cannot commit to
|
||||
`master`, please try to avoid using GitHub's merge button. Apply the change
|
||||
to `master` locally (feel free to amend any minor problems in the author's
|
||||
original commit if necessary).
|
||||
5. If the change is good, and authored by another maintainer/collaborator, give
|
||||
them a "Ship it!" comment and let them handle the merge.
|
||||
|
||||
### Submitting changes
|
||||
|
||||
1. All non-trivial changes should be put up for review using GitHub Pull
|
||||
Requests.
|
||||
2. Your change should not be merged into `master` (or another feature branch),
|
||||
without at least one "Ship it!" comment from another maintainer/collaborator
|
||||
on the project. "Looks good to me" is not the same as "Ship it!".
|
||||
3. Try to avoid using GitHub's merge button. Locally rebase your change onto
|
||||
`master` and then push to GitHub.
|
||||
4. Once a feature branch has been merged into its target branch, please delete
|
||||
the feature branch from the remote repository.
|
||||
|
||||
### Releasing a new version
|
||||
|
||||
1. Include all new functional changes in the CHANGELOG.
|
||||
2. Use a dedicated commit to increment the version. The version needs to be
|
||||
added to the `CHANGELOG.md` (inc. date) and the `package.json`.
|
||||
3. The commit message must be of `v0.0.0` format.
|
||||
4. Create an annotated tag for the version: `git tag -m "v0.0.0" v0.0.0`.
|
||||
5. Push the changes and tags to GitHub: `git push --tags origin master`.
|
||||
6. Publish the new version to npm: `npm publish`.
|
||||
20
LICENSE
20
LICENSE
@@ -1,7 +1,19 @@
|
||||
Copyright (c) 2012 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 the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
422
README.md
422
README.md
@@ -1,277 +1,249 @@
|
||||
BOWER [](http://travis-ci.org/twitter/bower)
|
||||
=====
|
||||
# Bower - A package manager for the web
|
||||
|
||||
### Introduction
|
||||
[](https://github.com/bower/bower/actions?query=branch%3Amaster)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
|
||||
Bower is a package manager for the web. Bower lets you easily install assets such as images, CSS and JavaScript, and manages dependencies for you.
|
||||
> ..psst! While Bower is maintained, we recommend [yarn](https://yarnpkg.com/) and [webpack](https://webpack.js.org/) or [parcel](https://parceljs.org/) for new front-end projects!
|
||||
|
||||
For example, to install a package, run:
|
||||
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
|
||||
|
||||
bower install jquery
|
||||
---
|
||||
|
||||
This will download jQuery to `./components/jquery`. That's it. The idea is that Bower does package management and package management only.
|
||||
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.
|
||||
|
||||
### Installing Bower
|
||||
Bower runs over Git, and is package-agnostic. A packaged component can be made up of any type of asset, and use any type of transport (e.g., AMD, CommonJS, etc.).
|
||||
|
||||
Bower is installed using [Node](http://nodejs.org/) and [npm](http://npmjs.org/) (oh my, how meta).
|
||||
**View complete docs on [bower.io](http://bower.io)**
|
||||
|
||||
npm install bower -g
|
||||
[View all packages available through Bower's registry](http://bower.io/search/).
|
||||
|
||||
### Usage
|
||||
## Install
|
||||
|
||||
Your best friend at this stage is probably `bower --help`.
|
||||
|
||||
To install a package:
|
||||
|
||||
bower install jquery
|
||||
bower install git://github.com/maccman/package-jquery.git
|
||||
bower install maccman/package-jquery (same as above)
|
||||
bower install http://code.jquery.com/jquery-1.7.2.js
|
||||
bower install ./repos/jquery
|
||||
|
||||
As you can see, packages can be installed by name, Git endpoint, GitHub shorthand, URL or local path.
|
||||
If you install and URL that is a zip or tar file, bower will automatically extract the contents of it.
|
||||
Bower also works with private Git repositories. Simply reference them by their SSH endpoint:
|
||||
|
||||
bower install git@github.com:user/private-package.git
|
||||
|
||||
[View all packages available through Bower's registry](http://sindresorhus.com/bower-components/).
|
||||
|
||||
During install you can have Bower add an entry to your component.json as well:
|
||||
|
||||
bower install --save jquery
|
||||
|
||||
To update a package, reference it by name:
|
||||
|
||||
bower update jquery-ui
|
||||
|
||||
To list installed packages:
|
||||
|
||||
bower list
|
||||
|
||||
To search for packages:
|
||||
|
||||
bower search [name]
|
||||
|
||||
To list all the available packages, just call `bower search` without specifying a name.
|
||||
|
||||
To clean the cache:
|
||||
|
||||
bower cache-clean [name]
|
||||
|
||||
Several packages can be cleaned at the same time.
|
||||
To clean the entire cache, just call `bower cache-clean` without any names.
|
||||
Also, both the install and update commands have a `--force` flag that tells bower to bypass the cache and always fetch remote sources.
|
||||
|
||||
You can disable colors by using the `--no-color` flag.
|
||||
|
||||
### Bower Configuration
|
||||
|
||||
Bower can be configured by creating a .bowerrc file in your home folder (usually ~/bowerrc) with one or all of the following configuration parameters. You can also configure Bower on a per-project basis by creating a .bowerrc file in the project directory, Bower will merge this configuration with the configuration found in your home directory. This allows you to version your project specific Bower configuration with the rest of your code base.
|
||||
|
||||
```json
|
||||
{
|
||||
"directory" : "components",
|
||||
"json" : "component.json",
|
||||
"endpoint" : "https://bower.herokuapp.com"
|
||||
}
|
||||
```sh
|
||||
$ npm install -g bower
|
||||
```
|
||||
|
||||
To run your own Bower Endpoint for custom components/packages that are behind a firewall you can use a simple implementation of bower server at https://github.com/twitter/bower-server.
|
||||
Bower depends on [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/). Also make sure that [git](http://git-scm.com/) is installed as some bower
|
||||
packages require it to be fetched and installed.
|
||||
|
||||
|
||||
### Defining a package
|
||||
## Usage
|
||||
|
||||
You can create a `component.json` file in your project's root, specifying all of its dependencies. This is similar to Node's `package.json`, or Ruby's `Gemfile`, and is useful for locking down a project's dependencies.
|
||||
See complete command line reference at [bower.io/docs/api/](http://bower.io/docs/api/)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "myProject",
|
||||
"version": "1.0.0",
|
||||
"main": "./path/to/main.css",
|
||||
"dependencies": {
|
||||
"jquery": "~1.7.2"
|
||||
}
|
||||
}
|
||||
### Installing packages and dependencies
|
||||
|
||||
```sh
|
||||
# install dependencies listed in bower.json
|
||||
$ bower install
|
||||
|
||||
# install a package and add it to bower.json
|
||||
$ bower install <package> --save
|
||||
|
||||
# install specific version of a package and add it to bower.json
|
||||
$ bower install <package>#<version> --save
|
||||
```
|
||||
|
||||
Put this under your project's root, listing all of your dependencies. When you run `bower install`, Bower will read this `component.json` file, resolve all the relevant dependencies and install them.
|
||||
### Using packages
|
||||
|
||||
For now, `name`, `version`, `main`, and `dependencies` are the only properties that are used by Bower. If you have several files you're distributing as part of your package, pass an array to `main` like this:
|
||||
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).
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "myProject",
|
||||
"version": "1.0.0",
|
||||
"main": ["./path/to/app.css", "./path/to/app.js", "./path/to/sprite.img"],
|
||||
"dependencies": {
|
||||
"jquery": "~1.7.2"
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
To uninstall a locally installed package:
|
||||
|
||||
```sh
|
||||
$ bower uninstall <package-name>
|
||||
```
|
||||
|
||||
Bower only recognizes versions that follow the [semver](http://semver.org/) specification.
|
||||
There should only be at most one file per file type in the `main` list. So only one `.js` or `.css`.
|
||||
### prezto and oh-my-zsh users
|
||||
|
||||
### Installing dependencies
|
||||
On `prezto` or `oh-my-zsh`, do not forget to `alias bower='noglob bower'` or `bower install jquery\#1.9.1`
|
||||
|
||||
Dependencies are installed locally via the `bower install` command. First they’re resolved to find conflicts. Then they’re downloaded and unpacked in a local subdirectory called `./components`, for example:
|
||||
### Never run Bower with sudo
|
||||
|
||||
|
||||
```
|
||||
/component.json
|
||||
/components/jquery/index.js
|
||||
/components/jquery/component.json
|
||||
```
|
||||
|
||||
You can also install packages one at a time `bower install git://my/git/thing`
|
||||
|
||||
There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
|
||||
|
||||
### Deploying
|
||||
|
||||
The easiest approach is to use Bower statically, just reference the packages manually from a script tag:
|
||||
|
||||
<script src="components/jquery/index.js"></script>
|
||||
|
||||
For more complex projects, you'll probably want to concatenate your scripts. Bower is just a package manager, but there are lots of awesome libraries out there to help you do this, such as [Sprockets](https://github.com/sstephenson/sprockets) and [RequireJS](http://requirejs.org/).
|
||||
|
||||
For example, to use Sprockets:
|
||||
|
||||
```ruby
|
||||
environment = Sprockets::Environment.new
|
||||
environment.append_path 'components'
|
||||
environment.append_path 'public'
|
||||
run environment
|
||||
```
|
||||
|
||||
### Package Consumption
|
||||
|
||||
Bower also makes available a source mapping – this can be used by build tools to easily consume Bower components.
|
||||
|
||||
If you pass the option `--map` to bower's `list` command, it will generate a JSON with dependency objects. Alternatively, you can pass the `--paths` flag to the `list` command to get a simple path to name mapping:
|
||||
|
||||
```json
|
||||
{
|
||||
"backbone": "components/backbone/index.js",
|
||||
"jquery": "components/jquery/index.js",
|
||||
"underscore": "components/underscore/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
### Authoring packages
|
||||
|
||||
To register a new package, it's as simple as specifying a `component.json`, pushing the package to a Git endpoint, say GitHub, and running:
|
||||
|
||||
bower register myawesomepackagename git://github.com/maccmans/face
|
||||
|
||||
There's no authentication or user management. It's on a first come, first served basis. Think of it like a URL shortener. Now anyone can run `bower install myawesomepackagename`, and get your library installed.
|
||||
|
||||
There is no direct way to unregister a package yet. Meanwhile you can request it [here](https://github.com/twitter/bower/issues/120).
|
||||
|
||||
### Philosophy
|
||||
|
||||
Currently, people are managing dependencies, such as JavaScript libraries, manually. This sucks, and we want to change it.
|
||||
|
||||
In a nutshell, Bower is a generic tool which will resolve dependencies and lock packages down to a version. It runs over Git, and is package-agnostic. A package may contain JavaScript, CSS, images, etc., and doesn't rely on any particular transport (AMD, CommonJS, etc.).
|
||||
|
||||
Bower then makes available a simple programmatic API which exposes the package dependency model, so that existing build tools (like Sprockets, LoadBuilder, curls.js, Ender, etc.) can consume it and build files accordingly.
|
||||
|
||||
|
||||
### Programmatic API
|
||||
|
||||
Bower provides a pretty powerful programmatic api. All commands can be accessed through the `bower.commands` object.
|
||||
|
||||
```js
|
||||
var bower = require('bower');
|
||||
|
||||
bower.commands
|
||||
.install(paths, options)
|
||||
.on('end', function (data) {
|
||||
data && console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
All commands emit three types of events: `data`, `end`, and `error`.
|
||||
|
||||
For a better of idea how this works, you may want to check out [our bin file](https://github.com/twitter/bower/blob/master/bin/bower).
|
||||
Bower is a user command; there is no need to execute it with superuser permissions.
|
||||
|
||||
### Windows users
|
||||
|
||||
A lot of people are experience problems using bower on windows because [msysgit](http://code.google.com/p/msysgit/) must be installed correctly.
|
||||
Be sure to check the option shown above, otherwise it will simply not work:
|
||||
To use Bower on Windows, you must install
|
||||
[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 -
|
||||
C:\Program Files\TortoiseGit\bin\TortoisePlink.exe`. Adjust the `TortoisePlink`
|
||||
path if needed.
|
||||
|
||||
### Ubuntu users
|
||||
|
||||
To use Bower on Ubuntu, you might need to link `nodejs` executable to `node`:
|
||||
|
||||
```
|
||||
sudo ln -s /usr/bin/nodejs /usr/bin/node
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Bower can be configured using JSON in a `.bowerrc` file. Read over available options at [bower.io/docs/config](http://bower.io/docs/config).
|
||||
|
||||
|
||||
### FAQ
|
||||
## Support
|
||||
|
||||
**What distinguishes Bower from Jam, Volo, Component, or Ender? What does it do better?**
|
||||
You can ask questions on following channels in order:
|
||||
|
||||
Bower is a lower level component than Jam, Volo, Component, or Ender. These managers could consume Bower as a dependency.
|
||||
* [StackOverflow](http://stackoverflow.com/questions/tagged/bower)
|
||||
* [Issue Tracker](https://github.com/bower/bower/issues)
|
||||
* team@bower.io
|
||||
|
||||
Bower's aim is simply to install Git paths, resolve dependencies from a `component.json`, check versions, and then provide an API which reports on these things. Nothing more. This is a major diversion from past attempts at browser package management.
|
||||
## Contributing
|
||||
|
||||
Bower is working under the assumption that there is a single, common problem in frontend application development: dependency resolution. Past attempts (Jam, Volo, Ender, Component) try to tackle this problem in such a way that they actually end up alienating and further segregating the JavaScript community around transports (Sprockets, CommonJS, RequireJS, regular script tags).
|
||||
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).
|
||||
|
||||
Bower offers a generic, unopinionated solution to the problem of package management, while exposing an API that can be consumed by a more opinionated build stack.
|
||||
* [Bug reports](https://github.com/bower/bower/wiki/Report-a-Bug)
|
||||
* [Feature requests](CONTRIBUTING.md#features)
|
||||
* [Pull requests](CONTRIBUTING.md#pull-requests)
|
||||
|
||||
|
||||
**Volo is an arguably more established project and works with the GitHub search API. Will it take long for Bower to contain a decent number of packages?**
|
||||
Note that on Windows for tests to pass you need to configure Git before cloning:
|
||||
|
||||
Bower (being a Git powered package manager) should, in theory, be capable of consuming every package that Volo does, with the additional benefit of supporting internal networks and other Git repositories not hosted on GitHub.
|
||||
|
||||
**We recently saw what happened when the main NPM registry went down. Is a single point of failure possible for Bower and if so, do you have redundancy planned?**
|
||||
|
||||
There's no redundancy planned at the moment, as Bower just installs Git URLs. It's up to the URL provider to establish redundancy.
|
||||
|
||||
**Isn't having a `package.json` file going to conflict with my npm's `package.json`? Will this be a problem?**
|
||||
|
||||
Don't use a `package.json` – use a `component.json`.
|
||||
|
||||
**Bower is an open-source Twitter project. How well can we expect it to be maintained in the future?**
|
||||
|
||||
Twitter is in the process of migrating its frontend architecture onto Bower, so it's fairly safe to say it will be maintained and invested in going forward.
|
||||
```
|
||||
git config --global core.autocrlf input
|
||||
```
|
||||
|
||||
|
||||
### Contact
|
||||
## Backers
|
||||
|
||||
Have a question? Ask on our mailing list!
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/bower#backer)]
|
||||
|
||||
twitter-bower@googlegroups.com
|
||||
<a href="https://opencollective.com/bower/backer/0/website" target="_blank"><img src="https://opencollective.com/bower/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/1/website" target="_blank"><img src="https://opencollective.com/bower/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/2/website" target="_blank"><img src="https://opencollective.com/bower/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/3/website" target="_blank"><img src="https://opencollective.com/bower/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/4/website" target="_blank"><img src="https://opencollective.com/bower/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/5/website" target="_blank"><img src="https://opencollective.com/bower/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/6/website" target="_blank"><img src="https://opencollective.com/bower/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/7/website" target="_blank"><img src="https://opencollective.com/bower/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/8/website" target="_blank"><img src="https://opencollective.com/bower/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/9/website" target="_blank"><img src="https://opencollective.com/bower/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/10/website" target="_blank"><img src="https://opencollective.com/bower/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/11/website" target="_blank"><img src="https://opencollective.com/bower/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/12/website" target="_blank"><img src="https://opencollective.com/bower/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/13/website" target="_blank"><img src="https://opencollective.com/bower/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/14/website" target="_blank"><img src="https://opencollective.com/bower/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/15/website" target="_blank"><img src="https://opencollective.com/bower/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/16/website" target="_blank"><img src="https://opencollective.com/bower/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/17/website" target="_blank"><img src="https://opencollective.com/bower/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/18/website" target="_blank"><img src="https://opencollective.com/bower/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/19/website" target="_blank"><img src="https://opencollective.com/bower/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/20/website" target="_blank"><img src="https://opencollective.com/bower/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/21/website" target="_blank"><img src="https://opencollective.com/bower/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/22/website" target="_blank"><img src="https://opencollective.com/bower/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/23/website" target="_blank"><img src="https://opencollective.com/bower/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/24/website" target="_blank"><img src="https://opencollective.com/bower/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/25/website" target="_blank"><img src="https://opencollective.com/bower/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/26/website" target="_blank"><img src="https://opencollective.com/bower/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/27/website" target="_blank"><img src="https://opencollective.com/bower/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/28/website" target="_blank"><img src="https://opencollective.com/bower/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/29/website" target="_blank"><img src="https://opencollective.com/bower/backer/29/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/30/website" target="_blank"><img src="https://opencollective.com/bower/backer/30/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/31/website" target="_blank"><img src="https://opencollective.com/bower/backer/31/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/32/website" target="_blank"><img src="https://opencollective.com/bower/backer/32/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/33/website" target="_blank"><img src="https://opencollective.com/bower/backer/33/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/34/website" target="_blank"><img src="https://opencollective.com/bower/backer/34/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/35/website" target="_blank"><img src="https://opencollective.com/bower/backer/35/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/36/website" target="_blank"><img src="https://opencollective.com/bower/backer/36/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/37/website" target="_blank"><img src="https://opencollective.com/bower/backer/37/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/38/website" target="_blank"><img src="https://opencollective.com/bower/backer/38/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/39/website" target="_blank"><img src="https://opencollective.com/bower/backer/39/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/40/website" target="_blank"><img src="https://opencollective.com/bower/backer/40/avatar.svg"></a>
|
||||
|
||||
http://groups.google.com/group/twitter-bower
|
||||
## Sponsors
|
||||
|
||||
### Authors
|
||||
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)]
|
||||
|
||||
+ [@fat](http://github.com/fat)
|
||||
+ [@maccman](http://github.com/maccman)
|
||||
+ [@satazor](http://github.com/satazor)
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/0/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/1/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/2/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/3/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/4/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/5/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/6/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/7/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/8/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/9/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/10/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/11/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/12/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/13/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/14/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/15/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/16/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/17/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/18/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/19/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/20/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/21/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/22/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/23/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/24/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/25/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/26/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/27/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/28/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/29/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/29/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/30/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/30/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/31/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/31/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/32/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/32/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/33/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/33/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/34/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/34/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/35/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/35/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/36/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/36/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/37/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/37/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/38/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/38/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/39/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/39/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/40/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/40/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/41/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/41/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/42/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/42/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/43/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/43/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/44/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/44/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/45/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/45/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/46/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/46/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/47/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/47/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/48/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/48/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/49/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/49/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/50/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/50/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/51/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/51/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/52/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/52/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/53/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/53/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/54/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/54/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/55/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/55/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/56/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/56/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/57/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/57/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/58/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/58/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/59/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/59/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/60/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/60/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/61/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/61/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/62/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/62/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/63/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/63/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/64/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/64/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/65/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/65/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/66/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/66/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/67/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/67/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/68/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/68/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/69/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/69/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/70/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/70/avatar.svg"></a>
|
||||
|
||||
Thanks for assistance and contributions:
|
||||
|
||||
+ [@addyosmani](http://github.com/addyosmani)
|
||||
+ [@angus-c](http://github.com/angus-c)
|
||||
+ [@borismus](http://github.com/borismus)
|
||||
+ [@chriseppstein](http://github.com/chriseppstein)
|
||||
+ [@danwrong](http://github.com/danwrong)
|
||||
+ [@desandro](http://github.com/desandro)
|
||||
+ [@isaacs](http://github.com/isaacs)
|
||||
+ [@josh](http://github.com/josh)
|
||||
+ [@jrburke](http://github.com/jrburke)
|
||||
+ [@mklabs](http://github.com/mklabs)
|
||||
+ [@paulirish](http://github.com/paulirish)
|
||||
+ [@rvagg](http://github.com/rvagg)
|
||||
+ [@sindresorhus](http://github.com/sindresorhus)
|
||||
+ [@SlexAxton](http://github.com/SlexAxton)
|
||||
+ [@sstephenson](http://github.com/sstephenson)
|
||||
+ [@tomdale](http://github.com/tomdale)
|
||||
+ [@visionmedia](http://github.com/visionmedia)
|
||||
+ [@wagenet](http://github.com/wagenet)
|
||||
+ [@wycats](http://github.com/wycats)
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2012 Twitter, Inc.
|
||||
Copyright (c) 2012-present Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
|
||||
|
||||
Licensed under the MIT License
|
||||
|
||||
3
SECURITY.md
Normal file
3
SECURITY.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Security Policy
|
||||
|
||||
For critical security issues, please send an e-mail to team@bower.io instead of filing issue here.
|
||||
43
bin/bower
43
bin/bower
@@ -1,44 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var semver = require('semver');
|
||||
var nopt = require('nopt');
|
||||
var path = require('path');
|
||||
var pkg = require(path.join(__dirname, '..', 'package.json'));
|
||||
|
||||
var template = require('../lib/util/template');
|
||||
var bower = require('../lib');
|
||||
|
||||
var command;
|
||||
var options;
|
||||
var shorthand;
|
||||
var input = process.argv;
|
||||
var cmdList = Object.keys(bower.commands);
|
||||
var nodeVer = process.version;
|
||||
var reqVer = pkg.engines.node;
|
||||
|
||||
process.title = 'bower';
|
||||
|
||||
if (reqVer && !semver.satisfies(nodeVer, reqVer)) {
|
||||
throw new Error('Required: node ' + reqVer);
|
||||
}
|
||||
|
||||
shorthand = { 'v': ['--version'] };
|
||||
options = { version: Boolean };
|
||||
options = nopt(options, shorthand, process.argv);
|
||||
|
||||
bower.version = pkg.version;
|
||||
|
||||
if (options.version) return console.log(bower.version);
|
||||
if (~cmdList.indexOf(command = options.argv.remain && options.argv.remain.shift())) bower.command = command;
|
||||
|
||||
bower.commands[bower.command || 'help'].line(input)
|
||||
.on('data', function (data) {
|
||||
if (data) console.log(data);
|
||||
})
|
||||
.on('end', function (data) {
|
||||
if (data) console.log(data);
|
||||
})
|
||||
.on('error', function (err) {
|
||||
if (options.verbose) throw err;
|
||||
else template('error', { message: err.message }).on('data', function (d) { console.log(d); });
|
||||
});
|
||||
require('../lib/bin/bower');
|
||||
|
||||
52
cleanup.js
52
cleanup.js
@@ -1,52 +0,0 @@
|
||||
var spawn = require('child_process').spawn;
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var rimraf = require('rimraf');
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
// Get the previous bower version
|
||||
var version = '';
|
||||
var cp = spawn('bower', ['-v']);
|
||||
|
||||
cp
|
||||
.stdout.on('data', function (data) {
|
||||
version += data;
|
||||
})
|
||||
.on('close', function (code) {
|
||||
version = version.replace(/\n$/, '');
|
||||
if (code || !version) return;
|
||||
|
||||
// The cache folders changed, so we cleanup the old cache structure
|
||||
var temp = process.env.TMPDIR
|
||||
|| process.env.TMP
|
||||
|| process.env.TEMP
|
||||
|| process.platform === 'win32' ? 'c:\\windows\\temp' : '/tmp';
|
||||
|
||||
var home = (process.platform === 'win32'
|
||||
? process.env.USERPROFILE
|
||||
: process.env.HOME) || temp;
|
||||
|
||||
var roaming = process.platform === 'win32'
|
||||
? path.resolve(process.env.APPDATA || home || temp)
|
||||
: path.resolve(home || temp);
|
||||
|
||||
// If on windows, delete the old bower-cache folder
|
||||
if (process.platform === 'win32') {
|
||||
return rimraf(path.join(roaming, 'bower-cache'), function (err) {
|
||||
if (err) console.log('Error deleting the deprecated bower-cache folder');
|
||||
});
|
||||
}
|
||||
|
||||
// If on linux / mac delete the contents inside the .bower folder, except the cache and links, need to be removed
|
||||
var folder = path.join(roaming, '.bower');
|
||||
fs.readdir(folder, function (err, files) {
|
||||
if (err) return;
|
||||
files = _.without(files, 'cache', 'links');
|
||||
files.forEach(function (file) {
|
||||
rimraf(path.join(folder, file), function (err) {
|
||||
if (err) console.log('Error deleting the deprecated .bower contents folder');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// ==========================================
|
||||
// BOWER: CacheClean API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var async = require('async');
|
||||
var nopt = require('nopt');
|
||||
var rimraf = require('rimraf');
|
||||
var path = require('path');
|
||||
var glob = require('glob');
|
||||
var _ = require('lodash');
|
||||
|
||||
var help = require('./help');
|
||||
var config = require('../core/config');
|
||||
var template = require('../util/template');
|
||||
var fileExists = require('../util/file-exists');
|
||||
|
||||
var optionTypes = { help: Boolean, force: Boolean };
|
||||
var shorthand = { 'h': ['--help'], 'S': ['--save'], 'f': ['--force'] };
|
||||
|
||||
var removePkg = function (pkg, emitter, next) {
|
||||
var folder = path.join(config.cache, pkg);
|
||||
|
||||
fileExists(folder, function (exists) {
|
||||
if (!exists) return emitter.emit('error', new Error('Package ' + pkg + ' is not installed'));
|
||||
|
||||
rimraf(folder, function (err) {
|
||||
if (err) emitter.emit('error', err);
|
||||
else {
|
||||
emitter.emit('data', template('action', { name: 'cleared', shizzle: pkg }, true));
|
||||
next();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var createFuncs = function (pkgs, emitter) {
|
||||
return pkgs.map(function (pkg) {
|
||||
pkg = pkg.replace(/^\.\//, '');
|
||||
return removePkg.bind(removePkg, pkg, emitter);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = function (pkgs) {
|
||||
var emitter = new Emitter;
|
||||
|
||||
// If no pkgs are passed we delete all
|
||||
// Otherwise we delete the passed ones
|
||||
if (!pkgs || !pkgs.length) {
|
||||
glob('./*', { cwd: config.cache }, function (err, dirs) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
pkgs = dirs;
|
||||
async.parallel(createFuncs(pkgs, emitter), emitter.emit.bind(emitter, 'end'));
|
||||
});
|
||||
} else {
|
||||
async.parallel(createFuncs(_.uniq(pkgs), emitter), emitter.emit.bind(emitter, 'end'));
|
||||
}
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var pkgs = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help) return help('cache-clean');
|
||||
return module.exports(pkgs, options);
|
||||
};
|
||||
195
lib/commands/cache/clean.js
vendored
Normal file
195
lib/commands/cache/clean.js
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
var fs = require('../../util/fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var rimraf = require('../../util/rimraf');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var PackageRepository = require('../../core/PackageRepository');
|
||||
var semver = require('../../util/semver');
|
||||
var defaultConfig = require('../../config');
|
||||
|
||||
function clean(logger, endpoints, options, config) {
|
||||
var decEndpoints;
|
||||
var names;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
|
||||
// If endpoints is an empty array, null them
|
||||
if (endpoints && !endpoints.length) {
|
||||
endpoints = null;
|
||||
}
|
||||
|
||||
// Generate decomposed endpoints and names based on the endpoints
|
||||
if (endpoints) {
|
||||
decEndpoints = endpoints.map(function(endpoint) {
|
||||
return endpointParser.decompose(endpoint);
|
||||
});
|
||||
names = decEndpoints.map(function(decEndpoint) {
|
||||
return decEndpoint.name || decEndpoint.source;
|
||||
});
|
||||
}
|
||||
|
||||
return Q.all([
|
||||
clearPackages(decEndpoints, config, logger),
|
||||
clearLinks(names, config, logger)
|
||||
]).spread(function(entries) {
|
||||
return entries;
|
||||
});
|
||||
}
|
||||
|
||||
function clearPackages(decEndpoints, config, logger) {
|
||||
var repository = new PackageRepository(config, logger);
|
||||
|
||||
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) {
|
||||
var entryPkgMeta = entry.pkgMeta;
|
||||
|
||||
// Check if name or source match the entry
|
||||
if (
|
||||
decEndpoint.name !== entryPkgMeta.name &&
|
||||
decEndpoint.source !== entryPkgMeta.name &&
|
||||
decEndpoint.source !== entryPkgMeta._source
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If target is a wildcard, simply return true
|
||||
if (decEndpoint.target === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it's a semver target, compare using semver spec
|
||||
if (semver.validRange(decEndpoint.target)) {
|
||||
return semver.satisfies(
|
||||
entryPkgMeta.version,
|
||||
decEndpoint.target
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, compare against target/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
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clearLinks(names, config, logger) {
|
||||
var promise;
|
||||
var dir = config.storage.links;
|
||||
|
||||
// If no names are passed, grab all links
|
||||
if (!names) {
|
||||
promise = Q.nfcall(fs.readdir, dir).fail(function(err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
// Otherwise use passed ones
|
||||
} else {
|
||||
promise = Q.resolve(names);
|
||||
}
|
||||
|
||||
return promise.then(function(names) {
|
||||
var promises;
|
||||
var linksToRemove = [];
|
||||
|
||||
// Decide which links to delete
|
||||
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()) {
|
||||
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() {
|
||||
var promises;
|
||||
|
||||
// Remove each link that was declared as invalid
|
||||
promises = linksToRemove.map(function(link) {
|
||||
return Q.nfcall(rimraf, link).then(function() {
|
||||
logger.info('deleted', 'Invalid link: ' + link, {
|
||||
file: link
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
clean.readOptions = function(argv) {
|
||||
var cli = require('../../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var endpoints = options.argv.remain.slice(2);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [endpoints, options];
|
||||
};
|
||||
|
||||
module.exports = clean;
|
||||
42
lib/commands/cache/list.js
vendored
Normal file
42
lib/commands/cache/list.js
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
var mout = require('mout');
|
||||
var PackageRepository = require('../../core/PackageRepository');
|
||||
var defaultConfig = require('../../config');
|
||||
|
||||
function list(logger, packages, options, config) {
|
||||
var repository;
|
||||
|
||||
config = defaultConfig(config);
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
// If packages is an empty array, null them
|
||||
if (packages && !packages.length) {
|
||||
packages = null;
|
||||
}
|
||||
|
||||
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) {
|
||||
return pkg === entry.pkgMeta.name;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return entries;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
list.readOptions = function(argv) {
|
||||
var cli = require('../../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var packages = options.argv.remain.slice(2);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [packages, options];
|
||||
};
|
||||
|
||||
module.exports = list;
|
||||
@@ -1,32 +1,41 @@
|
||||
// ==========================================
|
||||
// BOWER: Help API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var fs = require('../util/fs');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
var events = require('events');
|
||||
var nopt = require('nopt');
|
||||
var _ = require('lodash');
|
||||
function help(logger, name, config) {
|
||||
var json;
|
||||
|
||||
var template = require('../util/template');
|
||||
var config = require('../core/config');
|
||||
if (name) {
|
||||
json = path.resolve(
|
||||
__dirname,
|
||||
'../templates/json/help-' + name.replace(/\s+/g, '/') + '.json'
|
||||
);
|
||||
} else {
|
||||
json = path.resolve(__dirname, '../templates/json/help.json');
|
||||
}
|
||||
|
||||
module.exports = function (name) {
|
||||
var context = {};
|
||||
var emitter = new events.EventEmitter;
|
||||
var commands = require('../commands');
|
||||
var templateName = name ? 'help-' + name : 'help';
|
||||
return Q.promise(function(resolve) {
|
||||
fs.exists(json, resolve);
|
||||
}).then(function(exists) {
|
||||
if (!exists) {
|
||||
throw createError('Unknown command: ' + name, 'EUNKNOWNCMD', {
|
||||
command: name
|
||||
});
|
||||
}
|
||||
|
||||
if (!name) context = { commands: Object.keys(commands).join(', ') };
|
||||
_.extend(context, config);
|
||||
template(templateName, context).on('data', emitter.emit.bind(emitter, 'end'));
|
||||
return emitter;
|
||||
return require(json);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
help.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain.slice(1).join(' ');
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt({}, {}, argv);
|
||||
var paths = options.argv.remain.slice(1);
|
||||
return module.exports(paths[0]);
|
||||
};
|
||||
module.exports = help;
|
||||
|
||||
59
lib/commands/home.js
Normal file
59
lib/commands/home.js
Normal file
@@ -0,0 +1,59 @@
|
||||
var Project = require('../core/Project');
|
||||
var open = require('opn');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function home(logger, name, config) {
|
||||
var project;
|
||||
var promise;
|
||||
var decEndpoint;
|
||||
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
// Get the package meta
|
||||
// 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) {
|
||||
if (!json) {
|
||||
throw createError('You are not inside a package', 'ENOENT');
|
||||
}
|
||||
|
||||
return project.getJson();
|
||||
});
|
||||
} else {
|
||||
decEndpoint = endpointParser.decompose(name);
|
||||
promise = project
|
||||
.getPackageRepository()
|
||||
.fetch(decEndpoint)
|
||||
.spread(function(canonicalDir, pkgMeta) {
|
||||
return pkgMeta;
|
||||
});
|
||||
}
|
||||
|
||||
// Get homepage and open it
|
||||
return promise.then(function(pkgMeta) {
|
||||
var homepage = pkgMeta.homepage;
|
||||
|
||||
if (!homepage) {
|
||||
throw createError('No homepage set for ' + pkgMeta.name, 'ENOHOME');
|
||||
}
|
||||
|
||||
open(homepage, { wait: false });
|
||||
return homepage;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
home.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
module.exports = home;
|
||||
@@ -1,22 +1,82 @@
|
||||
// ==========================================
|
||||
// BOWER: Public Commands List
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Q = require('q');
|
||||
var Logger = require('bower-logger');
|
||||
var config = require('../config');
|
||||
|
||||
/**
|
||||
* Require commands only when called.
|
||||
*
|
||||
* Running `commandFactory(id)` is equivalent to `require(id)`. Both calls return
|
||||
* a command function. The difference is that `cmd = commandFactory()` and `cmd()`
|
||||
* return as soon as possible and load and execute the command asynchronously.
|
||||
*/
|
||||
function commandFactory(id) {
|
||||
function runApi() {
|
||||
var command = require(id);
|
||||
var commandArgs = [].slice.call(arguments);
|
||||
|
||||
return withLogger(function(logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
});
|
||||
}
|
||||
|
||||
function runFromArgv(argv) {
|
||||
var commandArgs;
|
||||
var command = require(id);
|
||||
|
||||
commandArgs = command.readOptions(argv);
|
||||
|
||||
return withLogger(function(logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
runApi.line = runFromArgv;
|
||||
|
||||
return runApi;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
'help': require('./help'),
|
||||
'install': require('./install'),
|
||||
'list': require('./list'),
|
||||
'ls': require('./list'),
|
||||
'uninstall': require('./uninstall'),
|
||||
'update': require('./update'),
|
||||
'link': require('./link'),
|
||||
'lookup': require('./lookup'),
|
||||
'info': require('./info'),
|
||||
'register': require('./register'),
|
||||
'search': require('./search'),
|
||||
'cache-clean': require('./cache-clean')
|
||||
};
|
||||
cache: {
|
||||
clean: commandFactory('./cache/clean'),
|
||||
list: commandFactory('./cache/list')
|
||||
},
|
||||
help: commandFactory('./help'),
|
||||
home: commandFactory('./home'),
|
||||
info: commandFactory('./info'),
|
||||
init: commandFactory('./init'),
|
||||
install: commandFactory('./install'),
|
||||
link: commandFactory('./link'),
|
||||
list: commandFactory('./list'),
|
||||
login: commandFactory('./login'),
|
||||
lookup: commandFactory('./lookup'),
|
||||
prune: commandFactory('./prune'),
|
||||
register: commandFactory('./register'),
|
||||
search: commandFactory('./search'),
|
||||
update: commandFactory('./update'),
|
||||
uninstall: commandFactory('./uninstall'),
|
||||
unregister: commandFactory('./unregister'),
|
||||
version: commandFactory('./version')
|
||||
};
|
||||
|
||||
@@ -1,45 +1,73 @@
|
||||
// ==========================================
|
||||
// BOWER: Lookup API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var nopt = require('nopt');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var template = require('../util/template');
|
||||
var source = require('../core/source');
|
||||
var help = require('./help');
|
||||
function info(logger, endpoint, property, config) {
|
||||
if (!endpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
var optionTypes = { help: Boolean };
|
||||
var shorthand = { 'h': ['--help'] };
|
||||
// handle @ as version divider
|
||||
var splitParts = endpoint.split('/');
|
||||
splitParts[splitParts.length - 1] = splitParts[
|
||||
splitParts.length - 1
|
||||
].replace('@', '#');
|
||||
endpoint = splitParts.join('/');
|
||||
|
||||
module.exports = function (name) {
|
||||
var emitter = new Emitter;
|
||||
var repository;
|
||||
var decEndpoint;
|
||||
|
||||
if (name) {
|
||||
source.info(name, function (err, result) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
emitter.emit('end', result);
|
||||
config = defaultConfig(config);
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
decEndpoint = endpointParser.decompose(endpoint);
|
||||
|
||||
return Q.all([
|
||||
getPkgMeta(repository, decEndpoint, property),
|
||||
decEndpoint.target === '*' && !property
|
||||
? repository.versions(decEndpoint.source)
|
||||
: null
|
||||
]).spread(function(pkgMeta, versions) {
|
||||
if (versions) {
|
||||
return {
|
||||
name: decEndpoint.source,
|
||||
versions: versions,
|
||||
latest: pkgMeta
|
||||
};
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return emitter;
|
||||
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) !== '_';
|
||||
});
|
||||
|
||||
// Retrieve specific property
|
||||
if (property) {
|
||||
pkgMeta = mout.object.get(pkgMeta, property);
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
info.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var pkg = options.argv.remain[1];
|
||||
var property = options.argv.remain[2];
|
||||
|
||||
return [pkg, property];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var emitter = new Emitter;
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help || !names.length) return help('info');
|
||||
|
||||
module.exports(names[0])
|
||||
.on('error', emitter.emit.bind(emitter, 'error'))
|
||||
.on('end', function (data) {
|
||||
template('info', data).on('data', emitter.emit.bind(emitter, 'end'));
|
||||
});
|
||||
|
||||
return emitter;
|
||||
};
|
||||
module.exports = info;
|
||||
|
||||
342
lib/commands/init.js
Normal file
342
lib/commands/init.js
Normal file
@@ -0,0 +1,342 @@
|
||||
var mout = require('mout');
|
||||
var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
var GitHubResolver = require('../core/resolvers/GitHubResolver');
|
||||
var cmd = require('../util/cmd');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function init(logger, config) {
|
||||
var project;
|
||||
|
||||
config = config || {};
|
||||
|
||||
if (!config.cwd) {
|
||||
config.cwd = process.cwd();
|
||||
}
|
||||
|
||||
config = defaultConfig(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'
|
||||
});
|
||||
}
|
||||
|
||||
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))
|
||||
);
|
||||
}
|
||||
|
||||
function readJson(project, logger) {
|
||||
return project.hasJson().then(function(json) {
|
||||
if (json) {
|
||||
logger.warn(
|
||||
'existing',
|
||||
'The existing ' +
|
||||
path.basename(json) +
|
||||
' file will be used and filled in'
|
||||
);
|
||||
}
|
||||
|
||||
return project.getJson();
|
||||
});
|
||||
}
|
||||
|
||||
function saveJson(project, logger, json) {
|
||||
// Cleanup empty props (null values, empty strings, objects and arrays)
|
||||
mout.object.forOwn(json, function(value, key) {
|
||||
if (!validConfigValue(value)) {
|
||||
delete json[key];
|
||||
}
|
||||
});
|
||||
|
||||
logger.info('json', 'Generated json', { json: json });
|
||||
|
||||
// Confirm the json with the user
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'Looks good?',
|
||||
default: true
|
||||
}).then(function(good) {
|
||||
if (!good) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Save json (true forces file creation)
|
||||
return project.saveJson(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Test if value is of a type supported by bower.json[0] - Object, Array, String, Boolean - or a Number
|
||||
// [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.isBoolean(val) ||
|
||||
mout.lang.isNumber(val)
|
||||
);
|
||||
}
|
||||
|
||||
function setDefaults(config, json) {
|
||||
var name;
|
||||
var promise = Q.resolve();
|
||||
|
||||
// Name
|
||||
if (!json.name) {
|
||||
json.name = path.basename(config.cwd);
|
||||
}
|
||||
|
||||
// Main
|
||||
if (!json.main) {
|
||||
// Remove '.js' from the end of the package name if it is there
|
||||
name = path.basename(json.name, '.js');
|
||||
|
||||
if (fs.existsSync(path.join(config.cwd, 'index.js'))) {
|
||||
json.main = 'index.js';
|
||||
} else if (fs.existsSync(path.join(config.cwd, name + '.js'))) {
|
||||
json.main = name + '.js';
|
||||
}
|
||||
}
|
||||
|
||||
// Homepage
|
||||
if (!json.homepage) {
|
||||
// Set as GitHub homepage if it's a GitHub repository
|
||||
promise = promise.then(function() {
|
||||
return cmd('git', ['config', '--get', 'remote.origin.url'])
|
||||
.spread(function(stdout) {
|
||||
var pair;
|
||||
|
||||
stdout = stdout.trim();
|
||||
if (!stdout) {
|
||||
return;
|
||||
}
|
||||
|
||||
pair = GitHubResolver.getOrgRepoPair(stdout);
|
||||
if (pair) {
|
||||
json.homepage =
|
||||
'https://github.com/' + pair.org + '/' + pair.repo;
|
||||
}
|
||||
})
|
||||
.fail(function() {});
|
||||
});
|
||||
}
|
||||
|
||||
if (!json.authors) {
|
||||
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();
|
||||
|
||||
// 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() {}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(function() {
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
function promptUser(logger, json) {
|
||||
var questions = [
|
||||
{
|
||||
name: 'name',
|
||||
message: 'name',
|
||||
default: json.name,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
message: 'description',
|
||||
default: json.description,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'main',
|
||||
message: 'main file',
|
||||
default: json.main,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
message: 'keywords',
|
||||
default: json.keywords ? json.keywords.toString() : null,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'authors',
|
||||
message: 'authors',
|
||||
default: json.authors ? json.authors.toString() : null,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'license',
|
||||
message: 'license',
|
||||
default: json.license || 'MIT',
|
||||
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: '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'
|
||||
}
|
||||
];
|
||||
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions).then(function(
|
||||
answers
|
||||
) {
|
||||
json.name = answers.name;
|
||||
json.description = answers.description;
|
||||
json.main = answers.main;
|
||||
json.keywords = toArray(answers.keywords);
|
||||
json.authors = toArray(answers.authors, ',');
|
||||
json.license = answers.license;
|
||||
json.homepage = answers.homepage;
|
||||
json.private = answers.private || null;
|
||||
|
||||
return [json, answers];
|
||||
});
|
||||
}
|
||||
|
||||
function toArray(value, splitter) {
|
||||
var arr = value.split(splitter || /[\s,]/);
|
||||
|
||||
// Trim values
|
||||
arr = arr.map(function(item) {
|
||||
return item.trim();
|
||||
});
|
||||
|
||||
// Filter empty values
|
||||
arr = arr.filter(function(item) {
|
||||
return !!item;
|
||||
});
|
||||
|
||||
return arr.length ? arr : null;
|
||||
}
|
||||
|
||||
function setIgnore(config, json, answers) {
|
||||
if (answers.ignore) {
|
||||
json.ignore = mout.array.combine(json.ignore || [], [
|
||||
'**/.*',
|
||||
'node_modules',
|
||||
'bower_components',
|
||||
config.directory,
|
||||
'test',
|
||||
'tests'
|
||||
]);
|
||||
}
|
||||
|
||||
return [json, answers];
|
||||
}
|
||||
|
||||
function setDependencies(project, json, answers) {
|
||||
if (answers.dependencies) {
|
||||
return project.getTree().spread(function(tree, flattened, extraneous) {
|
||||
if (extraneous.length) {
|
||||
json.dependencies = {};
|
||||
|
||||
// Add extraneous as dependencies
|
||||
extraneous.forEach(function(extra) {
|
||||
var jsonEndpoint;
|
||||
|
||||
// Skip linked packages
|
||||
if (extra.linked) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonEndpoint = endpointParser.decomposed2json(
|
||||
extra.endpoint
|
||||
);
|
||||
mout.object.mixIn(json.dependencies, jsonEndpoint);
|
||||
});
|
||||
}
|
||||
|
||||
return [json, answers];
|
||||
});
|
||||
}
|
||||
|
||||
return [json, answers];
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
init.readOptions = function(argv) {
|
||||
return [];
|
||||
};
|
||||
|
||||
module.exports = init;
|
||||
@@ -1,45 +1,55 @@
|
||||
// ==========================================
|
||||
// BOWER: Install API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
// 1. Recursively resolve dependencies
|
||||
// 2. Intelligently work out which deps to
|
||||
// use (versioning)
|
||||
// 3. Throw if deps conflict
|
||||
// ==========================================
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var nopt = require('nopt');
|
||||
function install(logger, endpoints, options, config) {
|
||||
var project;
|
||||
var decEndpoints;
|
||||
|
||||
var Manager = require('../core/manager');
|
||||
var save = require('../util/save');
|
||||
var help = require('./help');
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
if (options.save === undefined) {
|
||||
options.save = config.defaultSave;
|
||||
}
|
||||
project = new Project(config, logger);
|
||||
|
||||
var optionTypes = { help: Boolean, save: Boolean, force: Boolean };
|
||||
var shorthand = { 'h': ['--help'], 'S': ['--save'], 'f': ['--force'] };
|
||||
// Convert endpoints to decomposed endpoints
|
||||
endpoints = endpoints || [];
|
||||
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('/');
|
||||
|
||||
module.exports = function (paths, options) {
|
||||
var emitter = new Emitter;
|
||||
var manager = new Manager(paths, { force: options && options.force });
|
||||
return endpointParser.decompose(endpoint);
|
||||
});
|
||||
|
||||
if (options && options.save) save(manager, paths);
|
||||
return project.install(decEndpoints, options, config);
|
||||
}
|
||||
|
||||
manager
|
||||
.on('data', emitter.emit.bind(emitter, 'data'))
|
||||
.on('error', emitter.emit.bind(emitter, 'error'))
|
||||
.on('resolve', emitter.emit.bind(emitter, 'end', null))
|
||||
.resolve();
|
||||
// -------------------
|
||||
|
||||
return emitter;
|
||||
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 packages = options.argv.remain.slice(1);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [packages, options];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var paths = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help) return help('install');
|
||||
return module.exports(paths, options);
|
||||
};
|
||||
module.exports = install;
|
||||
|
||||
@@ -1,111 +1,88 @@
|
||||
// ==========================================
|
||||
// BOWER: Link API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var path = require('path');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var createLink = require('../util/createLink');
|
||||
var defaultConfig = require('../config');
|
||||
var relativeToBaseDir = require('../util/relativeToBaseDir');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var nopt = require('nopt');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('rimraf');
|
||||
|
||||
var Manager = require('../core/manager');
|
||||
var help = require('./help');
|
||||
var template = require('../util/template');
|
||||
var config = require('../core/config');
|
||||
var isRepo = require('../util/is-repo');
|
||||
|
||||
var optionTypes = { help: Boolean };
|
||||
var shorthand = { 'h': ['--help'] };
|
||||
|
||||
function linkSelf(emitter) {
|
||||
var manager = new Manager;
|
||||
|
||||
manager
|
||||
.on('error', emitter.emit.bind('error'))
|
||||
.once('loadJSON', function () {
|
||||
var destPath = path.join(config.links, manager.name);
|
||||
var srcPath = process.cwd();
|
||||
|
||||
deleteLink(destPath, function (err) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
|
||||
createLink(srcPath, destPath, function (err) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
|
||||
template('link', { src: srcPath, dest: destPath })
|
||||
.on('data', emitter.emit.bind(emitter, 'end'));
|
||||
});
|
||||
});
|
||||
}).loadJSON();
|
||||
function link(logger, name, localName, config) {
|
||||
if (name) {
|
||||
return linkTo(logger, name, localName, config);
|
||||
} else {
|
||||
return linkSelf(logger, config);
|
||||
}
|
||||
}
|
||||
|
||||
function linkTo(name, emitter) {
|
||||
var destPath = path.join(process.cwd(), config.directory, name);
|
||||
var srcPath = path.join(config.links, name);
|
||||
function linkSelf(logger, config) {
|
||||
var project;
|
||||
|
||||
deleteLink(destPath, function (err) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
createLink(srcPath, destPath, function (err) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
return project.getJson().then(function(json) {
|
||||
var src = config.cwd;
|
||||
var dst = path.join(config.storage.links, json.name);
|
||||
|
||||
template('link', { src: srcPath, dest: destPath })
|
||||
.on('data', emitter.emit.bind(emitter, 'end'));
|
||||
// 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
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteLink(dest, callback) {
|
||||
// Delete symlink if already present
|
||||
// Beware that if the target is a git repo, we can't proceed
|
||||
isRepo(dest, function (is) {
|
||||
if (is) return callback(new Error(dest + ' is a local repository, please remove it manually'));
|
||||
function linkTo(logger, name, localName, config) {
|
||||
var src;
|
||||
var dst;
|
||||
var project;
|
||||
|
||||
fs.lstat(dest, function (err) {
|
||||
if (!err || err.code !== 'ENOENT') rimraf(dest, callback);
|
||||
else callback();
|
||||
});
|
||||
});
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
localName = localName || name;
|
||||
src = path.join(config.storage.links, name);
|
||||
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
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function createLink(src, dest, callback) {
|
||||
var destDir = path.dirname(dest);
|
||||
// -------------------
|
||||
|
||||
// Create directory
|
||||
mkdirp(destDir, function (err) {
|
||||
if (err) return callback(err);
|
||||
link.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
var localName = options.argv.remain[2];
|
||||
|
||||
fs.lstat(src, function (err) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
return callback(new Error('Attempting to link an unknown package: ' + path.basename(src)));
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
fs.symlink(src, dest, 'dir', function (err) {
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function (name) {
|
||||
var emitter = new Emitter;
|
||||
|
||||
if (!name) linkSelf(emitter);
|
||||
else linkTo(name, emitter);
|
||||
|
||||
return emitter;
|
||||
return [name, localName];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
if (options.help) return help('link');
|
||||
return module.exports(name);
|
||||
};
|
||||
module.exports = link;
|
||||
|
||||
@@ -1,180 +1,185 @@
|
||||
// ==========================================
|
||||
// BOWER: List API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var semver = require('../util/semver');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var semver = require('semver');
|
||||
var archy = require('archy');
|
||||
var async = require('async');
|
||||
var nopt = require('nopt');
|
||||
var path = require('path');
|
||||
var _ = require('lodash');
|
||||
function list(logger, options, config) {
|
||||
var project;
|
||||
|
||||
var template = require('../util/template');
|
||||
var Manager = require('../core/manager');
|
||||
var Package = require('../core/package');
|
||||
var config = require('../core/config');
|
||||
var help = require('./help');
|
||||
options = options || {};
|
||||
|
||||
var shorthand = { 'h': ['--help'] };
|
||||
var optionTypes = { help: Boolean, paths: Boolean, map: Boolean };
|
||||
|
||||
var getTree = function (packages, subPackages, result) {
|
||||
result = result || {};
|
||||
|
||||
_.each(subPackages || packages, function (pkg) {
|
||||
|
||||
result[pkg.name] = {};
|
||||
|
||||
Object.keys(pkg.json.dependencies || {}).forEach(function (name) {
|
||||
result[pkg.name][name] = {};
|
||||
});
|
||||
|
||||
var subPackages = {};
|
||||
|
||||
Object.keys(pkg.json.dependencies || {}).forEach(function (name) {
|
||||
subPackages[name] = packages[name] || new Package(name, null);
|
||||
});
|
||||
|
||||
getTree(packages, subPackages, result[pkg.name]);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var generatePath = function (name, main) {
|
||||
if (typeof main === 'string') {
|
||||
return path.join(config.directory, name, main);
|
||||
} else if (_.isArray(main)) {
|
||||
main = main.map(function (main) { return generatePath(name, main); });
|
||||
return main.length === 1 ? main[0] : main;
|
||||
}
|
||||
};
|
||||
|
||||
var buildSource = function (pkg, shallow) {
|
||||
var result = {};
|
||||
|
||||
if (pkg) {
|
||||
['main', 'scripts', 'styles', 'templates', 'images'].forEach(function (type) {
|
||||
if (pkg.json[type]) result[type] = generatePath(pkg.name, pkg.json[type]);
|
||||
});
|
||||
}
|
||||
|
||||
if (shallow) {
|
||||
result.main = result.main ? result.main
|
||||
: result.scripts ? result.scripts
|
||||
: result.styles ? result.styles
|
||||
: result.templates ? result.templates
|
||||
: result.images ? result.images
|
||||
: generatePath(pkg.name, '');
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var shallowTree = function (packages, tree) {
|
||||
var result = {};
|
||||
|
||||
Object.keys(tree).forEach(function (packageName) {
|
||||
result[packageName] = buildSource(packages[packageName], true).main;
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var deepTree = function (packages, tree) {
|
||||
|
||||
var result = {};
|
||||
|
||||
Object.keys(tree).forEach(function (packageName) {
|
||||
|
||||
result[packageName] = {};
|
||||
result[packageName].source = buildSource(packages[packageName]);
|
||||
|
||||
if (Object.keys(tree[packageName]).length) {
|
||||
result[packageName].dependencies = deepTree(packages, tree[packageName]);
|
||||
// Make relative option true by default when used with paths
|
||||
if (options.paths && options.relative == null) {
|
||||
options.relative = true;
|
||||
}
|
||||
|
||||
});
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return result;
|
||||
};
|
||||
return project.getTree(options).spread(function(tree, flattened) {
|
||||
// Relativize paths
|
||||
// Also normalize paths on windows
|
||||
project.walkTree(
|
||||
tree,
|
||||
function(node) {
|
||||
if (node.missing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var getNodes = function (packages, tree) {
|
||||
return Object.keys(tree).map(function (key) {
|
||||
var version = packages[key] ? packages[key].version || '' : null;
|
||||
var upgrade;
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(
|
||||
config.cwd,
|
||||
node.canonicalDir
|
||||
);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
if (version && packages[key].tags.indexOf(version)) {
|
||||
upgrade = packages[key].tags[0];
|
||||
}
|
||||
// Note that we need to to parse the flattened tree because it might
|
||||
// contain additional packages
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (node.missing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.keys(tree[key]).length) {
|
||||
return {
|
||||
label: template('tree-branch', { 'package': key, version: version, upgrade: upgrade }, true),
|
||||
nodes: getNodes(packages, tree[key])
|
||||
};
|
||||
} else {
|
||||
return template('tree-branch', { 'package': key, version: version, upgrade: upgrade }, true);
|
||||
}
|
||||
});
|
||||
};
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(
|
||||
config.cwd,
|
||||
node.canonicalDir
|
||||
);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
}
|
||||
});
|
||||
|
||||
var cliTree = function (emitter, packages, tree) {
|
||||
emitter.emit('data', archy({
|
||||
label: process.cwd(),
|
||||
nodes: getNodes(packages, tree)
|
||||
}));
|
||||
};
|
||||
|
||||
module.exports = function (options) {
|
||||
var manager = new Manager;
|
||||
var emitter = new Emitter;
|
||||
|
||||
options = options || {};
|
||||
|
||||
manager
|
||||
.on('data', emitter.emit.bind(emitter, 'data'))
|
||||
.on('error', emitter.emit.bind(emitter, 'error'));
|
||||
|
||||
manager.once('resolveLocal', function () {
|
||||
var packages = {};
|
||||
|
||||
Object.keys(manager.dependencies).forEach(function (key) {
|
||||
packages[key] = manager.dependencies[key][0];
|
||||
});
|
||||
|
||||
async.forEach(_.values(packages), function (pkg, next) {
|
||||
pkg.once('loadJSON', function () {
|
||||
if (this.json.repository && (this.json.repository.type === 'git' || this.json.repository.type === 'local-repo')) {
|
||||
pkg.once('versions', function (versions) {
|
||||
pkg.tags = versions.map(function (ver) {
|
||||
return semver.valid(ver) ? semver.clean(ver) : ver;
|
||||
});
|
||||
next();
|
||||
}).versions();
|
||||
} else {
|
||||
pkg.tags = [];
|
||||
next();
|
||||
// Render paths?
|
||||
if (options.paths) {
|
||||
return paths(flattened);
|
||||
}
|
||||
}).loadJSON();
|
||||
}, function () {
|
||||
var tree = getTree(packages);
|
||||
if (!options.paths && !options.map && options.argv) return cliTree(emitter, packages, tree);
|
||||
tree = options.paths ? shallowTree(packages, tree) : deepTree(packages, tree);
|
||||
emitter.emit('data', options.argv ? JSON.stringify(tree, null, 2) : tree);
|
||||
});
|
||||
}).resolveLocal();
|
||||
|
||||
return emitter;
|
||||
// Do not check for new versions?
|
||||
if (config.offline) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
// Check for new versions
|
||||
return checkVersions(project, tree, logger).then(function() {
|
||||
return tree;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkVersions(project, tree, logger) {
|
||||
var promises;
|
||||
var nodes = [];
|
||||
var repository = project.getPackageRepository();
|
||||
|
||||
// Gather all nodes, ignoring linked nodes
|
||||
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...'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for new versions for each node
|
||||
promises = nodes.map(function(node) {
|
||||
var target = node.endpoint.target;
|
||||
|
||||
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, '*')
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Set the versions also for the root node
|
||||
tree.versions = [];
|
||||
|
||||
return Q.all(promises);
|
||||
}
|
||||
|
||||
function paths(flattened) {
|
||||
var ret = {};
|
||||
|
||||
mout.object.forOwn(flattened, function(pkg, name) {
|
||||
var main;
|
||||
|
||||
if (pkg.missing) {
|
||||
return;
|
||||
}
|
||||
|
||||
main = pkg.pkgMeta.main;
|
||||
|
||||
// If no main was specified, fallback to canonical dir
|
||||
if (!main) {
|
||||
ret[name] = pkg.canonicalDir;
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize main
|
||||
if (typeof main === 'string') {
|
||||
main = [main];
|
||||
}
|
||||
|
||||
// Concatenate each main entry with the canonical dir
|
||||
main = main.map(function(part) {
|
||||
return normalize(path.join(pkg.canonicalDir, part).trim());
|
||||
});
|
||||
|
||||
// If only one main file, use a string
|
||||
// Otherwise use an array
|
||||
ret[name] = main.length === 1 ? main[0] : main;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function normalize(src) {
|
||||
return src.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
list.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
paths: { type: Boolean, shorthand: 'p' },
|
||||
relative: { type: Boolean, shorthand: 'r' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
if (options.help) return help('list');
|
||||
return module.exports(options);
|
||||
};
|
||||
module.exports = list;
|
||||
|
||||
155
lib/commands/login.js
Normal file
155
lib/commands/login.js
Normal file
@@ -0,0 +1,155 @@
|
||||
var Configstore = require('configstore');
|
||||
var GitHub = require('github');
|
||||
var Q = require('q');
|
||||
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function login(logger, options, config) {
|
||||
var configstore = new Configstore('bower-github');
|
||||
|
||||
config = defaultConfig(config);
|
||||
|
||||
var promise;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (options.token) {
|
||||
promise = Q.resolve({ token: options.token });
|
||||
} 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'
|
||||
})
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var questions = [
|
||||
{
|
||||
name: 'username',
|
||||
message: 'Username',
|
||||
type: 'input',
|
||||
default: configstore.get('username')
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
message: 'Password',
|
||||
type: 'password'
|
||||
}
|
||||
];
|
||||
|
||||
var github = new GitHub({
|
||||
version: '3.0.0'
|
||||
});
|
||||
|
||||
promise = Q.nfcall(logger.prompt.bind(logger), questions).then(function(
|
||||
answers
|
||||
) {
|
||||
configstore.set('username', answers.username);
|
||||
|
||||
github.authenticate({
|
||||
type: 'basic',
|
||||
username: answers.username,
|
||||
password: answers.password
|
||||
});
|
||||
|
||||
return Q.ninvoke(github.authorization, 'create', {
|
||||
scopes: ['user', 'repo'],
|
||||
note:
|
||||
'Bower command line client (' +
|
||||
new Date().toISOString() +
|
||||
')'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(
|
||||
function(result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info(
|
||||
'EAUTH',
|
||||
'Logged in as ' + configstore.get('username'),
|
||||
{}
|
||||
);
|
||||
|
||||
return 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'
|
||||
}
|
||||
];
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
login.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
token: { type: String, shorthand: 't' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
};
|
||||
|
||||
module.exports = login;
|
||||
@@ -1,49 +1,37 @@
|
||||
// ==========================================
|
||||
// BOWER: Lookup API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Q = require('q');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var nopt = require('nopt');
|
||||
|
||||
var template = require('../util/template');
|
||||
var source = require('../core/source');
|
||||
var help = require('./help');
|
||||
|
||||
var optionTypes = { help: Boolean };
|
||||
var shorthand = { 'h': ['--help'] };
|
||||
|
||||
module.exports = function (name) {
|
||||
var emitter = new Emitter;
|
||||
|
||||
source.lookup(name, function (err, url) {
|
||||
if (err) {
|
||||
source.search(name, function (err, packages) {
|
||||
if (packages.length) {
|
||||
template('suggestions', {packages: packages, name: name})
|
||||
.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
} else {
|
||||
template('warning-missing', {name: name})
|
||||
.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
template('lookup', {name: name, url: url})
|
||||
.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
function lookup(logger, name, config) {
|
||||
if (!name) {
|
||||
return new Q(null);
|
||||
}
|
||||
});
|
||||
|
||||
return emitter;
|
||||
config = defaultConfig(config);
|
||||
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), name).then(
|
||||
function(entry) {
|
||||
return !entry
|
||||
? null
|
||||
: {
|
||||
name: name,
|
||||
url: entry.url
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
lookup.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help || !names.length) return help('lookup');
|
||||
return module.exports(names[0]);
|
||||
};
|
||||
module.exports = lookup;
|
||||
|
||||
60
lib/commands/prune.js
Normal file
60
lib/commands/prune.js
Normal file
@@ -0,0 +1,60 @@
|
||||
var mout = require('mout');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function prune(logger, options, config) {
|
||||
var project;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return clean(project, options);
|
||||
}
|
||||
|
||||
function clean(project, options, removed) {
|
||||
removed = 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;
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
prune.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
production: { type: Boolean, shorthand: 'p' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
};
|
||||
|
||||
module.exports = prune;
|
||||
@@ -1,50 +1,100 @@
|
||||
// ==========================================
|
||||
// BOWER: Lookup API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Q = require('q');
|
||||
var chalk = require('chalk');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var nopt = require('nopt');
|
||||
var readline = require('readline');
|
||||
function register(logger, name, source, config) {
|
||||
var repository;
|
||||
var registryClient;
|
||||
var force;
|
||||
var url;
|
||||
var githubSourceRegex = /^\w[\w-]*\/\w[\w-]*$/;
|
||||
var getGithubUrl = function(source) {
|
||||
return 'git@github.com:' + source + '.git';
|
||||
};
|
||||
|
||||
var template = require('../util/template');
|
||||
var source = require('../core/source');
|
||||
var help = require('./help');
|
||||
config = defaultConfig(config);
|
||||
force = config.force;
|
||||
|
||||
name = (name || '').trim();
|
||||
source = (source || '').trim();
|
||||
|
||||
var optionTypes = { help: Boolean };
|
||||
var shorthand = { 'h': ['--help'] };
|
||||
url = source.match(githubSourceRegex) ? getGithubUrl(source) : source;
|
||||
|
||||
module.exports = function (name, url) {
|
||||
var emitter = new Emitter;
|
||||
var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
// Bypass any cache
|
||||
config.offline = false;
|
||||
config.force = true;
|
||||
|
||||
console.log('Registering a package will make it visible and installable via the registry.');
|
||||
rl.question('Proceed (y/n)? ', function (res) {
|
||||
rl.close();
|
||||
return Q.try(function() {
|
||||
// Verify name and url
|
||||
if (!name || !url) {
|
||||
throw createError(
|
||||
'Usage: bower register <name> <url>',
|
||||
'EINVFORMAT'
|
||||
);
|
||||
}
|
||||
|
||||
res = res.toLowerCase();
|
||||
// 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'
|
||||
);
|
||||
}
|
||||
|
||||
if (res === 'y' || res === 'yes') {
|
||||
source.register(name, url, function (err) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template('register', {name: name, url: url})
|
||||
.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
});
|
||||
}
|
||||
});
|
||||
// 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;
|
||||
}
|
||||
|
||||
return emitter;
|
||||
// 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) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
var url = options.argv.remain[2];
|
||||
|
||||
return [name, url];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var args = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help || args.length !== 2) return help('register');
|
||||
return module.exports(args[0], args[1]);
|
||||
};
|
||||
module.exports = register;
|
||||
|
||||
@@ -1,49 +1,39 @@
|
||||
// ==========================================
|
||||
// BOWER: Lookup API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Q = require('q');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var defaultConfig = require('../config');
|
||||
var cli = require('../util/cli');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var nopt = require('nopt');
|
||||
function search(logger, name, config) {
|
||||
var registryClient;
|
||||
|
||||
var template = require('../util/template');
|
||||
var source = require('../core/source');
|
||||
var help = require('./help');
|
||||
config = defaultConfig(config);
|
||||
|
||||
var optionTypes = { help: Boolean };
|
||||
var shorthand = { 'h': ['--help'] };
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
|
||||
module.exports = function (name) {
|
||||
var emitter = new Emitter;
|
||||
|
||||
var callback = function (err, results) {
|
||||
if (err) return emitter.emit('error', err);
|
||||
|
||||
if (results.length) {
|
||||
template('search', {results: results})
|
||||
.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
if (name) {
|
||||
return Q.nfcall(registryClient.search.bind(registryClient), name);
|
||||
} else {
|
||||
template('search-empty', {results: results})
|
||||
.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
// 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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (name) {
|
||||
source.search(name, callback);
|
||||
} else {
|
||||
source.all(callback);
|
||||
}
|
||||
// -------------------
|
||||
|
||||
return emitter;
|
||||
search.readOptions = function(argv) {
|
||||
var options = cli.readOptions(argv);
|
||||
var terms = options.argv.remain.slice(1);
|
||||
|
||||
var name = terms.join(' ');
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help) return help('search');
|
||||
return module.exports(names[0]);
|
||||
};
|
||||
module.exports = search;
|
||||
|
||||
@@ -1,152 +1,132 @@
|
||||
// ==========================================
|
||||
// BOWER: Uninstall API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var async = require('async');
|
||||
var nopt = require('nopt');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var _ = require('lodash');
|
||||
function uninstall(logger, names, options, config) {
|
||||
if (!names.length) {
|
||||
return new Q();
|
||||
}
|
||||
|
||||
var template = require('../util/template');
|
||||
var Manager = require('../core/manager');
|
||||
var config = require('../core/config');
|
||||
var help = require('./help');
|
||||
var project;
|
||||
|
||||
var optionTypes = { help: Boolean, force: Boolean, save: Boolean };
|
||||
var shorthand = { 'h': ['--help'], 'S': ['--save'], 'f': ['--force'] };
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
module.exports = function (names, options) {
|
||||
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 = [];
|
||||
|
||||
var packages, uninstallables, packagesCount = {};
|
||||
var emitter = new Emitter;
|
||||
var manager = new Manager;
|
||||
var force = !!options.force;
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
manager.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
manager.on('error', emitter.emit.bind(emitter, 'error'));
|
||||
|
||||
var resolveLocal = function () {
|
||||
packages = _.flatten(_.values(manager.dependencies));
|
||||
uninstallables = packages.filter(function (pkg) {
|
||||
return _.include(names, pkg.name);
|
||||
// Clean them!
|
||||
return clean(project, children, uninstalled);
|
||||
})
|
||||
);
|
||||
});
|
||||
async.forEach(packages, function (pkg, next) {
|
||||
pkg.once('loadJSON', next).loadJSON();
|
||||
}, function () {
|
||||
if (showWarnings(force) && !force) return;
|
||||
includeShared();
|
||||
uninstall();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var showWarnings = function (force) {
|
||||
var foundConflicts = false;
|
||||
function clean(project, names, removed) {
|
||||
removed = removed || {};
|
||||
|
||||
packages.forEach(function (pkg) {
|
||||
if (!pkg.json.dependencies) return;
|
||||
if (uninstallables.indexOf(pkg) !== -1) return;
|
||||
return project.getTree().spread(function(tree, flattened) {
|
||||
var nodes = [];
|
||||
var dependantsCounter = {};
|
||||
|
||||
var conflicts = _.intersection(
|
||||
Object.keys(pkg.json.dependencies),
|
||||
_.pluck(uninstallables, 'name')
|
||||
);
|
||||
// Grab the nodes of each specified name
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (names.indexOf(node.endpoint.name) !== -1) {
|
||||
nodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
if (conflicts.length) {
|
||||
foundConflicts = true;
|
||||
if (!force) {
|
||||
conflicts.forEach(function (conflictName) {
|
||||
emitter.emit('data', template('warning-uninstall', { packageName: pkg.name, conflictName: conflictName }, true));
|
||||
});
|
||||
// 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
|
||||
);
|
||||
|
||||
// Filter out those that have no dependants
|
||||
nodes = nodes.filter(function(node) {
|
||||
return !dependantsCounter[node.endpoint.name];
|
||||
});
|
||||
|
||||
// Are we done?
|
||||
if (!nodes.length) {
|
||||
return Q.resolve(removed);
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the nodes after filtering
|
||||
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;
|
||||
|
||||
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)
|
||||
);
|
||||
});
|
||||
|
||||
// Recurse!
|
||||
return clean(project, children, removed);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (foundConflicts && !force) {
|
||||
emitter.emit('data', template('warn', { message: 'To proceed, run uninstall with the --force flag'}, true));
|
||||
}
|
||||
// -------------------
|
||||
|
||||
return foundConflicts;
|
||||
};
|
||||
uninstall.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var includeShared = function () {
|
||||
count(packages, packagesCount);
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
save: { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
uninstallables.forEach(function (pkg) {
|
||||
parseUninstallableDeps(pkg);
|
||||
});
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
for (var name in packagesCount) {
|
||||
var pkg = manager.dependencies[name][0];
|
||||
var jsonDeps = manager.json.dependencies ? manager.json.dependencies : {};
|
||||
delete options.argv;
|
||||
|
||||
if ((!packagesCount[name] || (packagesCount[name] === 1 && !jsonDeps[name]))
|
||||
&& uninstallables.indexOf(pkg) === -1) {
|
||||
if (packagesCount[name] > 0) packagesCount[name] -= 1;
|
||||
uninstallables.push(pkg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var count = function (packages, counts) {
|
||||
packages.forEach(function (pkg) {
|
||||
counts[pkg.name] = (counts[pkg.name] || 0) + 1;
|
||||
|
||||
if (pkg.json.dependencies) {
|
||||
for (var key in pkg.json.dependencies) {
|
||||
count(manager.dependencies[key], counts);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var parseUninstallableDeps = function (pkg) {
|
||||
if (packagesCount[pkg.name] > 0) packagesCount[pkg.name] -= 1;
|
||||
|
||||
if (pkg.json.dependencies) {
|
||||
for (var key in pkg.json.dependencies) {
|
||||
parseUninstallableDeps(manager.dependencies[key][0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var uninstall = function () {
|
||||
async.forEach(uninstallables, function (pkg, next) {
|
||||
pkg.on('uninstall', next).uninstall();
|
||||
}, function () {
|
||||
// Finally save
|
||||
if (options.save) save();
|
||||
emitter.emit.bind(emitter, 'end');
|
||||
});
|
||||
};
|
||||
|
||||
var save = function () {
|
||||
if (manager.json.dependencies) {
|
||||
names.forEach(function (name) {
|
||||
delete manager.json.dependencies[name];
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.join(manager.cwd, config.json), JSON.stringify(manager.json, null, 2));
|
||||
}
|
||||
};
|
||||
|
||||
manager.on('loadJSON', function () {
|
||||
manager.on('resolveLocal', resolveLocal).resolveLocal();
|
||||
}).loadJSON();
|
||||
|
||||
return emitter;
|
||||
return [names, options];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
|
||||
if (options.help) return help('uninstall');
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
return module.exports(names, options);
|
||||
};
|
||||
module.exports = uninstall;
|
||||
|
||||
90
lib/commands/unregister.js
Normal file
90
lib/commands/unregister.js
Normal file
@@ -0,0 +1,90 @@
|
||||
var chalk = require('chalk');
|
||||
var Q = require('q');
|
||||
|
||||
var defaultConfig = require('../config');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function unregister(logger, name, config) {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
var repository;
|
||||
var registryClient;
|
||||
var force;
|
||||
|
||||
config = defaultConfig(config);
|
||||
force = config.force;
|
||||
|
||||
// Bypass any cache
|
||||
config.offline = false;
|
||||
config.force = true;
|
||||
|
||||
// Trim name
|
||||
name = name.trim();
|
||||
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
if (!config.accessToken) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
unregister.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
module.exports = unregister;
|
||||
@@ -1,78 +1,39 @@
|
||||
// ==========================================
|
||||
// BOWER: Update API
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var async = require('async');
|
||||
var nopt = require('nopt');
|
||||
var _ = require('lodash');
|
||||
function update(logger, names, options, config) {
|
||||
var project;
|
||||
|
||||
var Manager = require('../core/manager');
|
||||
var help = require('./help');
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
var shorthand = { 'h': ['--help'], 'f': ['--force'] };
|
||||
var optionTypes = { help: Boolean, force: Boolean };
|
||||
// If names is an empty array, null them
|
||||
if (names && !names.length) {
|
||||
names = null;
|
||||
}
|
||||
|
||||
module.exports = function (names, options) {
|
||||
var manager = new Manager([], { force: options && options.force });
|
||||
var emitter = new Emitter;
|
||||
return project.update(names, options);
|
||||
}
|
||||
|
||||
manager.on('data', emitter.emit.bind(emitter, 'data'));
|
||||
manager.on('error', emitter.emit.bind(emitter, 'error'));
|
||||
// -------------------
|
||||
|
||||
var installURLS = function (err, arr) {
|
||||
var mappings = {},
|
||||
endpoints = [];
|
||||
update.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
arr = _.compact(arr);
|
||||
_.each(arr, function (info) {
|
||||
endpoints.push(info.endpoint);
|
||||
mappings[info.endpoint] = info.name;
|
||||
});
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
'force-latest': { type: Boolean, shorthand: 'F' },
|
||||
production: { type: Boolean, shorthand: 'p' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
options.endpointNames = mappings;
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
// By default the manager will guess the name of the package from the url
|
||||
// But this leads to problems when the package name does not match the one in the url
|
||||
// So the manager now has an option (endpointNames) to deal with this
|
||||
manager = new Manager(endpoints, options);
|
||||
manager
|
||||
.on('data', emitter.emit.bind(emitter, 'data'))
|
||||
.on('error', emitter.emit.bind(emitter, 'error'))
|
||||
.on('resolve', emitter.emit.bind(emitter, 'end', null))
|
||||
.resolve();
|
||||
};
|
||||
delete options.argv;
|
||||
|
||||
manager.once('resolveLocal', function () {
|
||||
names = names.length ? _.uniq(names) : null;
|
||||
|
||||
async.map(_.values(manager.dependencies), function (pkgs, next) {
|
||||
var pkg = pkgs[0];
|
||||
pkg.once('loadJSON', function () {
|
||||
pkg.once('fetchEndpoint', function (endpoint) {
|
||||
if (!endpoint) return next();
|
||||
|
||||
if (!pkg.json.commit || pkg.json.version !== '0.0.0') {
|
||||
endpoint += '#' + ((!names || names.indexOf(pkg.name) > -1) ? '~' : '') + pkg.version;
|
||||
}
|
||||
|
||||
next(null, { name: pkg.name, endpoint: endpoint });
|
||||
}).fetchEndpoint();
|
||||
}).loadJSON();
|
||||
}, installURLS);
|
||||
}).resolveLocal();
|
||||
|
||||
return emitter;
|
||||
return [names, options];
|
||||
};
|
||||
|
||||
module.exports.line = function (argv) {
|
||||
var options = nopt(optionTypes, shorthand, argv);
|
||||
if (options.help) return help('update');
|
||||
|
||||
var paths = options.argv.remain.slice(1);
|
||||
return module.exports(paths, options);
|
||||
};
|
||||
module.exports = update;
|
||||
|
||||
210
lib/commands/version.js
Normal file
210
lib/commands/version.js
Normal file
@@ -0,0 +1,210 @@
|
||||
var semver = require('semver');
|
||||
var which = require('which');
|
||||
var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var execFile = require('child_process').execFile;
|
||||
var defaultConfig = require('../config');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function version(logger, versionArg, options, config) {
|
||||
options = options || {};
|
||||
|
||||
config = defaultConfig(config);
|
||||
|
||||
return bump(logger, config, versionArg, options.message);
|
||||
}
|
||||
|
||||
function bump(logger, config, versionArg, message) {
|
||||
var cwd = config.cwd || process.cwd();
|
||||
var newVersion;
|
||||
|
||||
if (!versionArg) {
|
||||
throw createError('No <version> agrument provided', 'EREADOPTIONS');
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
function filterModifiedStatusLines(stdout) {
|
||||
return stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(function(line) {
|
||||
return line.trim() && !line.match(/^\?\? /);
|
||||
})
|
||||
.map(function(line) {
|
||||
return line.trim();
|
||||
});
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
return [options.argv.remain[1], options];
|
||||
};
|
||||
|
||||
module.exports = version;
|
||||
63
lib/config.js
Normal file
63
lib/config.js
Normal file
@@ -0,0 +1,63 @@
|
||||
var tty = require('tty');
|
||||
var object = require('mout').object;
|
||||
var bowerConfig = require('bower-config');
|
||||
var Configstore = require('configstore');
|
||||
|
||||
var current;
|
||||
|
||||
function defaultConfig(config) {
|
||||
config = config || {};
|
||||
|
||||
return readCachedConfig(config.cwd || process.cwd(), config);
|
||||
}
|
||||
|
||||
function readCachedConfig(cwd, overwrites) {
|
||||
current = bowerConfig.create(cwd).load(overwrites);
|
||||
|
||||
var config = current.toObject();
|
||||
|
||||
var configstore = new Configstore('bower-github').all;
|
||||
|
||||
object.mixIn(config, configstore);
|
||||
|
||||
// If interactive is auto (null), guess its value
|
||||
if (config.interactive == null) {
|
||||
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' }
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
function restoreConfig() {
|
||||
if (current) {
|
||||
current.restore();
|
||||
}
|
||||
}
|
||||
|
||||
function resetCache() {
|
||||
restoreConfig();
|
||||
current = undefined;
|
||||
}
|
||||
|
||||
module.exports = defaultConfig;
|
||||
module.exports.restore = restoreConfig;
|
||||
module.exports.reset = resetCache;
|
||||
1284
lib/core/Manager.js
Normal file
1284
lib/core/Manager.js
Normal file
File diff suppressed because it is too large
Load Diff
304
lib/core/PackageRepository.js
Normal file
304
lib/core/PackageRepository.js
Normal file
@@ -0,0 +1,304 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
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;
|
||||
|
||||
this._config = config;
|
||||
this._logger = 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
|
||||
this._resolveCache = new ResolveCache(this._config);
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
PackageRepository.prototype.fetch = function(decEndpoint) {
|
||||
var logger;
|
||||
var that = this;
|
||||
var isTargetable;
|
||||
var info = {
|
||||
decEndpoint: decEndpoint
|
||||
};
|
||||
|
||||
// Create a new logger that pipes everything to ours that will be
|
||||
// used to fetch
|
||||
logger = this._logger.geminate();
|
||||
// Intercept all logs, adding additional information
|
||||
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;
|
||||
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
// Otherwise, fetch remotely
|
||||
return resolver.constructor.versions(resolver.getSource());
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.eliminate = function(pkgMeta) {
|
||||
return Q.all([
|
||||
this._resolveCache.eliminate(pkgMeta),
|
||||
Q.nfcall(
|
||||
this._registryClient.clearCache.bind(this._registryClient),
|
||||
pkgMeta.name
|
||||
)
|
||||
]);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.clear = function() {
|
||||
return Q.all([
|
||||
this._resolveCache.clear(),
|
||||
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient))
|
||||
]);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.reset = function() {
|
||||
this._resolveCache.reset();
|
||||
this._registryClient.resetCache();
|
||||
};
|
||||
|
||||
PackageRepository.prototype.list = function() {
|
||||
return this._resolveCache.list();
|
||||
};
|
||||
|
||||
PackageRepository.prototype.getRegistryClient = function() {
|
||||
return this._registryClient;
|
||||
};
|
||||
|
||||
PackageRepository.prototype.getResolveCache = function() {
|
||||
return this._resolveCache;
|
||||
};
|
||||
|
||||
PackageRepository.clearRuntimeCache = function() {
|
||||
ResolveCache.clearRuntimeCache();
|
||||
RegistryClient.clearRuntimeCache();
|
||||
resolverFactory.clearRuntimeCache();
|
||||
};
|
||||
|
||||
// ---------------------
|
||||
|
||||
PackageRepository.prototype._getResolver = function(decEndpoint, logger) {
|
||||
logger = logger || this._logger;
|
||||
|
||||
// Get the appropriate resolver
|
||||
return resolverFactory(
|
||||
decEndpoint,
|
||||
{ config: this._config, logger: logger },
|
||||
this._registryClient
|
||||
);
|
||||
};
|
||||
|
||||
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 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()];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
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'
|
||||
]);
|
||||
}
|
||||
|
||||
// Store the resolver info in each log
|
||||
if (info.resolver) {
|
||||
log.data.resolver = {
|
||||
name: info.resolver.getName(),
|
||||
source: info.resolver.getSource(),
|
||||
target: info.resolver.getTarget()
|
||||
};
|
||||
}
|
||||
|
||||
// Store the canonical dir and its meta in each log
|
||||
if (info.canonicalDir) {
|
||||
log.data.canonicalDir = info.canonicalDir;
|
||||
log.data.pkgMeta = info.pkgMeta;
|
||||
}
|
||||
|
||||
return log;
|
||||
};
|
||||
|
||||
module.exports = PackageRepository;
|
||||
1033
lib/core/Project.js
Normal file
1033
lib/core/Project.js
Normal file
File diff suppressed because it is too large
Load Diff
440
lib/core/ResolveCache.js
Normal file
440
lib/core/ResolveCache.js
Normal file
@@ -0,0 +1,440 @@
|
||||
var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var LRU = require('lru-cache');
|
||||
var lockFile = require('lockfile');
|
||||
var md5 = require('md5-hex');
|
||||
var semver = require('../util/semver');
|
||||
var readJson = require('../util/readJson');
|
||||
var copy = require('../util/copy');
|
||||
|
||||
function ResolveCache(config) {
|
||||
// TODO: Make some config entries, such as:
|
||||
// - Max MB
|
||||
// - Max versions per source
|
||||
// - Max MB per source
|
||||
// - etc..
|
||||
this._config = config;
|
||||
this._dir = this._config.storage.packages;
|
||||
this._lockDir = this._config.storage.packages;
|
||||
|
||||
mkdirp.sync(this._lockDir);
|
||||
|
||||
// Cache is stored/retrieved statically to ensure singularity
|
||||
// among instances
|
||||
this._cache = this.constructor._cache.get(this._dir);
|
||||
if (!this._cache) {
|
||||
this._cache = new LRU({
|
||||
max: 100,
|
||||
maxAge: 60 * 5 * 1000 // 5 minutes
|
||||
});
|
||||
this.constructor._cache.set(this._dir, this._cache);
|
||||
}
|
||||
|
||||
// Ensure dir is created
|
||||
mkdirp.sync(this._dir);
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
ResolveCache.prototype.retrieve = function(source, target) {
|
||||
var sourceId = md5(source);
|
||||
var dir = path.join(this._dir, sourceId);
|
||||
var that = this;
|
||||
|
||||
target = target || '*';
|
||||
|
||||
return this._getVersions(sourceId)
|
||||
.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 (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';
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise check if there's an exact match
|
||||
return mout.array.find(versions, function(version) {
|
||||
return version === 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) {
|
||||
var sourceId;
|
||||
var release;
|
||||
var dir;
|
||||
var pkgLock;
|
||||
var promise;
|
||||
var that = this;
|
||||
|
||||
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'
|
||||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
})
|
||||
.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) {
|
||||
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() {
|
||||
var versions = that._cache.get(sourceId) || [];
|
||||
mout.array.remove(versions, release);
|
||||
|
||||
// If this was the last package in the cache,
|
||||
// delete the parent folder (source)
|
||||
// For extra security, check against the file system
|
||||
// if this was really the last package
|
||||
if (!versions.length) {
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return that._getVersions(sourceId).spread(function(versions) {
|
||||
if (!versions.length) {
|
||||
// Do not keep in-memory cache if it's completely
|
||||
// empty
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.nfcall(rimraf, path.dirname(dir));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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)
|
||||
);
|
||||
};
|
||||
|
||||
ResolveCache.prototype.reset = function() {
|
||||
this._cache.reset();
|
||||
return this;
|
||||
};
|
||||
|
||||
ResolveCache.prototype.versions = function(source) {
|
||||
var sourceId = md5(source);
|
||||
|
||||
return this._getVersions(sourceId).spread(function(versions) {
|
||||
return versions.filter(function(version) {
|
||||
return semver.valid(version);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
},
|
||||
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.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 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 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);
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
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) {
|
||||
cache.reset();
|
||||
});
|
||||
|
||||
// Clear root cache
|
||||
this._cache.reset();
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache.prototype._getPkgRelease = function(pkgMeta) {
|
||||
var release =
|
||||
pkgMeta.version ||
|
||||
(pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
|
||||
|
||||
// Encode some dangerous chars such as / and \
|
||||
release = encodeURIComponent(release);
|
||||
|
||||
return release;
|
||||
};
|
||||
|
||||
ResolveCache.prototype._readPkgMeta = function(dir) {
|
||||
var filename = path.join(dir, '.bower.json');
|
||||
|
||||
return readJson(filename).spread(function(json) {
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype._getVersions = function(sourceId) {
|
||||
var dir;
|
||||
var versions = this._cache.get(sourceId);
|
||||
var that = this;
|
||||
|
||||
if (versions) {
|
||||
return Q.resolve([versions, true]);
|
||||
}
|
||||
|
||||
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 = [];
|
||||
that._cache.set(sourceId, versions);
|
||||
return [versions, false];
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
ResolveCache.prototype._sortVersions = function(versions) {
|
||||
// Sort DESC
|
||||
versions.sort(function(version1, version2) {
|
||||
var validSemver1 = semver.valid(version1);
|
||||
var validSemver2 = semver.valid(version2);
|
||||
|
||||
// If both are semvers, compare them
|
||||
if (validSemver1 && validSemver2) {
|
||||
return semver.rcompare(version1, version2);
|
||||
}
|
||||
|
||||
// If one of them are semvers, give higher priority
|
||||
if (validSemver1) {
|
||||
return -1;
|
||||
}
|
||||
if (validSemver2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Otherwise they are considered equal
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache._cache = new LRU({
|
||||
max: 5,
|
||||
maxAge: 60 * 30 * 1000 // 30 minutes
|
||||
});
|
||||
|
||||
module.exports = ResolveCache;
|
||||
@@ -1,44 +0,0 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var tmp = require('tmp');
|
||||
|
||||
var fileExists = require('../util/file-exists').sync;
|
||||
|
||||
var temp = process.env.TMPDIR
|
||||
|| process.env.TMP
|
||||
|| process.env.TEMP
|
||||
|| process.platform === 'win32' ? 'c:\\windows\\temp' : '/tmp';
|
||||
|
||||
var home = (process.platform === 'win32'
|
||||
? process.env.USERPROFILE
|
||||
: process.env.HOME) || temp;
|
||||
|
||||
var roaming = process.platform === 'win32'
|
||||
? path.resolve(process.env.APPDATA || home || temp)
|
||||
: path.resolve(home || temp);
|
||||
|
||||
var folder = process.platform === 'win32'
|
||||
? 'bower'
|
||||
: '.bower';
|
||||
|
||||
// Bower Config
|
||||
var config = require('rc') ('bower', {
|
||||
cache : path.join(roaming, folder, 'cache'),
|
||||
links : path.join(roaming, folder, 'links'),
|
||||
json : 'component.json',
|
||||
endpoint : 'https://bower.herokuapp.com',
|
||||
directory : 'components'
|
||||
});
|
||||
|
||||
// If there is a local .bowerrc file, merge it
|
||||
var localFile = path.join(this.cwd, '.bowerrc');
|
||||
if (fileExists(localFile)) {
|
||||
_.extend(config, JSON.parse(fs.readFileSync(localFile)));
|
||||
}
|
||||
|
||||
// Configure tmp package to use graceful degradationn
|
||||
// If an uncaught exception occurs, the temporary directories will be deleted nevertheless
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
module.exports = config;
|
||||
@@ -1,216 +0,0 @@
|
||||
// ==========================================
|
||||
// BOWER: Manager Object Definition
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
// Events:
|
||||
// - install: fired when package installed
|
||||
// - resolve: fired when deps resolved (with a true/false indicating success or error)
|
||||
// - error: fired on all errors
|
||||
// - data: fired when trying to output data
|
||||
// - end: fired when finished installing
|
||||
// ==========================================
|
||||
|
||||
var events = require('events');
|
||||
var async = require('async');
|
||||
var path = require('path');
|
||||
var glob = require('glob');
|
||||
var fs = require('fs');
|
||||
|
||||
var Package = require('./package');
|
||||
var UnitWork = require('./unit_work');
|
||||
var config = require('./config');
|
||||
var fileExists = require('../util/file-exists');
|
||||
var template = require('../util/template');
|
||||
var prune = require('../util/prune');
|
||||
|
||||
// read local dependencies (with versions)
|
||||
// read json dependencies (resolving along the way into temp dir)
|
||||
// merge local dependencies with json dependencies
|
||||
// prune and move dependencies into local directory
|
||||
|
||||
var Manager = function (endpoints, opts) {
|
||||
this.dependencies = {};
|
||||
this.cwd = process.cwd();
|
||||
this.endpoints = endpoints || [];
|
||||
this.unitWork = new UnitWork;
|
||||
this.opts = opts || {};
|
||||
this.errors = [];
|
||||
};
|
||||
|
||||
Manager.prototype = Object.create(events.EventEmitter.prototype);
|
||||
Manager.prototype.constructor = Manager;
|
||||
|
||||
Manager.prototype.loadJSON = function () {
|
||||
var json = path.join(this.cwd, config.json);
|
||||
fileExists(json, function (exists) {
|
||||
if (!exists) {
|
||||
// If the json does not exist, assume one
|
||||
this.json = {
|
||||
name: path.basename(this.cwd),
|
||||
version: '0.0.0'
|
||||
},
|
||||
this.name = this.json.name;
|
||||
this.version = this.json.version;
|
||||
return this.emit('loadJSON');
|
||||
}
|
||||
|
||||
fs.readFile(json, 'utf8', function (err, json) {
|
||||
if (err) return this.emit('error', err);
|
||||
this.json = JSON.parse(json);
|
||||
this.name = this.json.name;
|
||||
this.version = this.json.version;
|
||||
this.emit('loadJSON');
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Manager.prototype.resolve = function () {
|
||||
var resolved = function () {
|
||||
// If there is errors, report them
|
||||
if (this.errors.length) return this.reportErrors();
|
||||
// If there is an error while pruning (conflict) then abort installation
|
||||
if (!this.prune()) return this.emit('resolve', false);
|
||||
// Otherwise all is fine, so we install
|
||||
this.once('install', this.emit.bind(this, 'resolve', true)).install();
|
||||
}.bind(this);
|
||||
|
||||
this.once('resolveLocal', function () {
|
||||
if (this.endpoints.length) {
|
||||
this.once('resolveEndpoints', resolved).resolveEndpoints();
|
||||
} else {
|
||||
this.once('resolveFromJson', resolved).resolveFromJson();
|
||||
}
|
||||
}).resolveLocal();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Manager.prototype.resolveLocal = function () {
|
||||
glob('./' + config.directory + '/*', function (err, dirs) {
|
||||
if (err) return this.emit('error', err);
|
||||
dirs.forEach(function (dir) {
|
||||
var name = path.basename(dir);
|
||||
var pkg = new Package(name, dir, this);
|
||||
this.dependencies[name] = [];
|
||||
this.dependencies[name].push(pkg);
|
||||
pkg.on('error', function (err, origin) {
|
||||
this.errors.push({ pkg: origin, error: err });
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
this.emit('resolveLocal');
|
||||
}.bind(this));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Manager.prototype.resolveEndpoints = function () {
|
||||
// Iterate through paths
|
||||
// Add to depedencies array
|
||||
// Prune & install
|
||||
|
||||
var endpointNames = this.opts.endpointNames || {};
|
||||
|
||||
async.forEach(this.endpoints, function (endpoint, next) {
|
||||
var name = endpointNames[endpoint];
|
||||
var pkg = new Package(name, endpoint, this);
|
||||
this.dependencies[name] = this.dependencies[name] || [];
|
||||
this.dependencies[name].push(pkg);
|
||||
pkg.on('error', function (err, origin) {
|
||||
this.errors.push({ pkg: origin, error: err });
|
||||
next();
|
||||
}.bind(this));
|
||||
pkg.once('resolve', next).resolve();
|
||||
}.bind(this), this.emit.bind(this, 'resolveEndpoints'));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Manager.prototype.resolveFromJson = function () {
|
||||
// loadJSON
|
||||
// Resolve dependencies
|
||||
// Add to dependencies array
|
||||
// Prune & install
|
||||
|
||||
this.once('loadJSON', function () {
|
||||
if (!this.json.dependencies) return this.emit('error', new Error('Could not find any dependencies'));
|
||||
|
||||
async.forEach(Object.keys(this.json.dependencies), function (name, next) {
|
||||
var endpoint = this.json.dependencies[name];
|
||||
var pkg = new Package(name, endpoint, this);
|
||||
this.dependencies[name] = this.dependencies[name] || [];
|
||||
this.dependencies[name].push(pkg);
|
||||
pkg.on('error', function (err, origin) {
|
||||
this.errors.push({ pkg: origin, error: err });
|
||||
next();
|
||||
}.bind(this));
|
||||
pkg.once('resolve', next).resolve();
|
||||
}.bind(this), this.emit.bind(this, 'resolveFromJson'));
|
||||
|
||||
}.bind(this)).loadJSON();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Private
|
||||
Manager.prototype.getDeepDependencies = function () {
|
||||
var result = {};
|
||||
|
||||
for (var name in this.dependencies) {
|
||||
this.dependencies[name].forEach(function (pkg) {
|
||||
result[pkg.name] = result[pkg.name] || [];
|
||||
result[pkg.name].push(pkg);
|
||||
pkg.getDeepDependencies().forEach(function (pkg) {
|
||||
result[pkg.name] = result[pkg.name] || [];
|
||||
result[pkg.name].push(pkg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Manager.prototype.prune = function () {
|
||||
try {
|
||||
this.dependencies = prune(this.getDeepDependencies());
|
||||
} catch (err) {
|
||||
this.emit('error', err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Manager.prototype.install = function () {
|
||||
async.forEach(Object.keys(this.dependencies), function (name, next) {
|
||||
var pkg = this.dependencies[name][0];
|
||||
pkg.once('install', next).install();
|
||||
pkg.once('error', next);
|
||||
}.bind(this), function () {
|
||||
if (this.errors.length) this.reportErrors();
|
||||
return this.emit('install');
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Manager.prototype.muteDependencies = function () {
|
||||
for (var name in this.dependencies) {
|
||||
this.dependencies[name].forEach(function (pkg) {
|
||||
pkg.removeAllListeners();
|
||||
pkg.on('error', function () {});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Manager.prototype.reportErrors = function () {
|
||||
template('error-summary', { errors: this.errors }).on('data', function (data) {
|
||||
this.muteDependencies();
|
||||
|
||||
this.emit('data', data);
|
||||
this.emit('resolve', false);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
module.exports = Manager;
|
||||
@@ -1,722 +0,0 @@
|
||||
// ==========================================
|
||||
// BOWER: Package Object Definition
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
// Events:
|
||||
// - install: fired when package installed
|
||||
// - resolve: fired when deps resolved
|
||||
// - error: fired on all errors
|
||||
// - data: fired when trying to output data
|
||||
// ==========================================
|
||||
|
||||
var fstream = require('fstream');
|
||||
var mkdirp = require('mkdirp');
|
||||
var events = require('events');
|
||||
var rimraf = require('rimraf');
|
||||
var semver = require('semver');
|
||||
var async = require('async');
|
||||
var https = require('https');
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
var tmp = require('tmp');
|
||||
var fs = require('fs');
|
||||
var crypto = require('crypto');
|
||||
var unzip = require('unzip');
|
||||
var tar = require('tar');
|
||||
|
||||
var config = require('./config');
|
||||
var source = require('./source');
|
||||
var template = require('../util/template');
|
||||
var readJSON = require('../util/read-json');
|
||||
var fileExists = require('../util/file-exists');
|
||||
var isRepo = require('../util/is-repo');
|
||||
var spawn = require('../util/spawn');
|
||||
var UnitWork = require('./unit_work');
|
||||
|
||||
var Package = function (name, endpoint, manager) {
|
||||
this.dependencies = {};
|
||||
this.json = {};
|
||||
this.name = name;
|
||||
this.manager = manager;
|
||||
this.unitWork = manager ? manager.unitWork : new UnitWork;
|
||||
this.opts = manager ? manager.opts : {};
|
||||
|
||||
if (endpoint) {
|
||||
var split;
|
||||
|
||||
if (/^(.*\.git)$/.exec(endpoint)) {
|
||||
this.gitUrl = RegExp.$1.replace(/^git\+/, '');
|
||||
this.tag = false;
|
||||
|
||||
} else if (/^(.*\.git)#(.*)$/.exec(endpoint)) {
|
||||
this.tag = RegExp.$2;
|
||||
this.gitUrl = RegExp.$1.replace(/^git\+/, '');
|
||||
|
||||
} else if (/^(?:(git):|git\+(https?):)\/\/([^#]+)#?(.*)$/.exec(endpoint)) {
|
||||
this.gitUrl = (RegExp.$1 || RegExp.$2) + '://' + RegExp.$3;
|
||||
this.tag = RegExp.$4;
|
||||
|
||||
} else if (semver.validRange(endpoint)) {
|
||||
this.tag = endpoint;
|
||||
|
||||
} else if (/^[\.\/~]\.?[^.]*\.(js|css)/.test(endpoint) && fs.statSync(endpoint).isFile()) {
|
||||
this.path = path.resolve(endpoint);
|
||||
this.assetType = path.extname(endpoint);
|
||||
|
||||
} else if (/^https?:\/\//.exec(endpoint)) {
|
||||
this.assetUrl = endpoint;
|
||||
this.assetType = path.extname(endpoint);
|
||||
|
||||
} else if (fileExists.sync((split = endpoint.split('#', 2))[0]) && fs.statSync(split[0]).isDirectory()) {
|
||||
this.path = path.resolve(split[0]);
|
||||
this.tag = split[1];
|
||||
|
||||
} else if (/^[\.\/~]/.test(endpoint)) {
|
||||
this.path = path.resolve(endpoint);
|
||||
|
||||
} else if (endpoint.split('/').length === 2) {
|
||||
split = endpoint.split('#', 2);
|
||||
this.gitUrl = 'git://github.com/' + split[0] + '.git';
|
||||
this.tag = split[1];
|
||||
} else {
|
||||
split = endpoint.split('#', 2);
|
||||
this.tag = split[1];
|
||||
}
|
||||
|
||||
// Guess names
|
||||
if (!this.name) {
|
||||
if (this.gitUrl) this.name = path.basename(endpoint).replace(/(\.git)?(#.*)?$/, '');
|
||||
else if (this.path) this.name = path.basename(this.path, this.assetType);
|
||||
else if (this.assetUrl) this.name = this.name = path.basename(this.assetUrl, this.assetType);
|
||||
else if (split) this.name = split[0];
|
||||
}
|
||||
|
||||
// Store a reference to the original tag & original path
|
||||
// This is because the tag & paths can get rewriten later
|
||||
if (this.tag) this.originalTag = this.tag;
|
||||
if (this.path) this.originalPath = endpoint;
|
||||
|
||||
// The id is an unique id that describes this package
|
||||
this.id = crypto.createHash('md5').update(this.name + '%' + this.tag + '%' + this.gitUrl + '%' + this.path + '%' + this.assetUrl).digest('hex');
|
||||
|
||||
// Generate a resource id
|
||||
if (this.gitUrl) this.generateResourceId();
|
||||
}
|
||||
|
||||
if (this.manager) {
|
||||
this.on('data', this.manager.emit.bind(this.manager, 'data'));
|
||||
this.on('error', function (err, origin) {
|
||||
// Unlock the unit of work automatically on error
|
||||
if (!origin && this.unitWork.isLocked(this.name)) this.unitWork.unlock(this.name, this);
|
||||
this.manager.emit('error', err, origin || this);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// Cache a self bound function
|
||||
this.waitUnlock = this.waitUnlock.bind(this);
|
||||
|
||||
this.setMaxListeners(30); // Increase the number of listeners because a package can have more than the default 10 dependencies
|
||||
};
|
||||
|
||||
Package.prototype = Object.create(events.EventEmitter.prototype);
|
||||
|
||||
Package.prototype.constructor = Package;
|
||||
|
||||
Package.prototype.resolve = function () {
|
||||
// Ensure that nobody is resolving the same dep at the same time
|
||||
// If there is, we wait for the unlock event
|
||||
if (this.unitWork.isLocked(this.name)) return this.unitWork.on('unlock', this.waitUnlock);
|
||||
|
||||
var data = this.unitWork.retrieve(this.name);
|
||||
if (data) {
|
||||
// Check if this exact package is the last resolved one
|
||||
// If so, we copy the resolved result and we don't need to do anything else
|
||||
if (data.id === this.id) {
|
||||
this.unserialize(data);
|
||||
this.emit('resolve');
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// If not, we lock and resolve it
|
||||
this.unitWork.lock(this.name, this);
|
||||
|
||||
if (this.assetUrl) {
|
||||
this.download();
|
||||
} else if (this.gitUrl) {
|
||||
this.clone();
|
||||
} else if (this.path) {
|
||||
this.copy();
|
||||
} else {
|
||||
this.once('lookup', this.clone).lookup();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Package.prototype.lookup = function () {
|
||||
source.lookup(this.name, function (err, url) {
|
||||
if (err) return this.emit('error', err);
|
||||
this.lookedUp = true;
|
||||
this.gitUrl = url;
|
||||
this.generateResourceId();
|
||||
this.emit('lookup');
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.install = function () {
|
||||
// Only print the installing action if this package has been resolved
|
||||
if (this.unitWork.retrieve(this.name)) {
|
||||
template('action', { name: 'installing', shizzle: this.name + (this.version ? '#' + this.version : '') })
|
||||
.on('data', this.emit.bind(this, 'data'));
|
||||
}
|
||||
|
||||
var localPath = this.localPath;
|
||||
|
||||
if (path.resolve(this.path) === localPath) {
|
||||
this.emit('install');
|
||||
return this;
|
||||
}
|
||||
|
||||
// Remove stuff from the local path (if any)
|
||||
// Rename path to the local path
|
||||
// Beware that if the local path exists and is a git repository, the process is aborted
|
||||
isRepo(localPath, function (is) {
|
||||
if (is) {
|
||||
var err = new Error('Local path is a local repository');
|
||||
err.details = 'To avoid loosing work, please remove ' + localPath + ' manually.';
|
||||
return this.emit('error', err, this);
|
||||
}
|
||||
|
||||
mkdirp(path.dirname(localPath), function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
rimraf(localPath, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
return fs.rename(this.path, localPath, function (err) {
|
||||
if (!err) return this.cleanUpLocal();
|
||||
|
||||
var writter = fstream.Writer({
|
||||
type: 'Directory',
|
||||
path: localPath
|
||||
});
|
||||
writter
|
||||
.on('error', this.emit.bind(this, 'error'))
|
||||
.on('end', rimraf.bind(this, this.path, this.cleanUpLocal.bind(this)));
|
||||
|
||||
fstream.Reader(this.path)
|
||||
.on('error', this.emit.bind(this, 'error'))
|
||||
.pipe(writter);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Package.prototype.cleanUpLocal = function () {
|
||||
this.json.name = this.name;
|
||||
this.json.version = this.commit ? '0.0.0' : this.version || '0.0.0';
|
||||
|
||||
// Detect commit and save it in the json for later use
|
||||
if (this.commit) this.json.commit = this.commit;
|
||||
else delete this.json.commit;
|
||||
|
||||
if (this.gitUrl) this.json.repository = { type: 'git', url: this.gitUrl };
|
||||
else if (this.gitPath) this.json.repository = { type: 'local-repo', path: this.originalPath };
|
||||
else if (this.originalPath) this.json.repository = { type: 'local', path: this.originalPath };
|
||||
else if (this.assetUrl) this.json = this.generateAssetJSON();
|
||||
|
||||
var jsonStr = JSON.stringify(this.json, null, 2);
|
||||
fs.writeFile(path.join(this.localPath, config.json), jsonStr);
|
||||
if (this.gitUrl || this.gitPath) fs.writeFile(path.join(this.gitPath, config.json), jsonStr);
|
||||
|
||||
rimraf(path.join(this.localPath, '.git'), this.emit.bind(this, 'install'));
|
||||
};
|
||||
|
||||
Package.prototype.generateAssetJSON = function () {
|
||||
return {
|
||||
name: this.name,
|
||||
main: this.assetType !== '.zip' && this.assetType !== '.tar' ? 'index' + this.assetType : '',
|
||||
version: '0.0.0',
|
||||
repository: { type: 'asset', url: this.assetUrl }
|
||||
};
|
||||
};
|
||||
|
||||
Package.prototype.uninstall = function () {
|
||||
template('action', { name: 'uninstalling', shizzle: this.path })
|
||||
.on('data', this.emit.bind(this, 'data'));
|
||||
rimraf(this.path, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
this.emit('uninstall');
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// Private
|
||||
Package.prototype.loadJSON = function () {
|
||||
if (!this.path || this.assetUrl) return this.emit('loadJSON');
|
||||
|
||||
var jsonFile = path.join(this.path, config.json);
|
||||
fileExists(jsonFile, function (exists) {
|
||||
// If the json does not exists, we attempt to at least get the version
|
||||
if (!exists) {
|
||||
return this.once('describeTag', function (tag) {
|
||||
tag = semver.clean(tag);
|
||||
if (!tag) this.version = this.tag;
|
||||
else {
|
||||
this.version = tag;
|
||||
if (!this.tag) this.tag = this.version;
|
||||
}
|
||||
|
||||
this.emit('loadJSON');
|
||||
}.bind(this)).describeTag();
|
||||
}
|
||||
|
||||
readJSON(jsonFile, function (err, json) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
this.json = json;
|
||||
this.version = this.commit || json.commit || json.version;
|
||||
this.commit = this.commit || json.commit;
|
||||
// Only overwrite the name if not already set
|
||||
// This is because some packages have different names declared in the registry and the json
|
||||
if (!this.name) this.name = json.name;
|
||||
|
||||
// Read the endpoint from the json to ensure it is set correctly
|
||||
this.readEndpoint();
|
||||
|
||||
// Detect if the tag mismatches the json.version
|
||||
// This is very often to happen because developers tag their new releases but forget to update the json accordingly
|
||||
var cleanedTag;
|
||||
if (this.tag && (cleanedTag = semver.clean(this.tag)) && cleanedTag !== this.version) {
|
||||
// Only print the warning once
|
||||
if (!this.unitWork.retrieve('mismatch#' + this.name + '_' + cleanedTag)) {
|
||||
template('warning-mismatch', { name: this.name, json: config.json, tag: cleanedTag, version: this.version || 'N/A' })
|
||||
.on('data', this.emit.bind(this, 'data'));
|
||||
this.unitWork.store('mismatch#' + this.name + '_' + cleanedTag, true);
|
||||
}
|
||||
// Assume the tag
|
||||
this.version = cleanedTag;
|
||||
}
|
||||
|
||||
this.emit('loadJSON');
|
||||
}.bind(this), this);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.download = function () {
|
||||
template('action', { name: 'downloading', shizzle: this.assetUrl })
|
||||
.on('data', this.emit.bind(this, 'data'));
|
||||
|
||||
var src = url.parse(this.assetUrl);
|
||||
var req = src.protocol === 'https:' ? https : http;
|
||||
|
||||
if (process.env.HTTP_PROXY) {
|
||||
src = url.parse(process.env.HTTP_PROXY);
|
||||
src.path = this.assetUrl;
|
||||
}
|
||||
|
||||
tmp.dir({ prefix: 'bower-' + this.name + '-', mode: parseInt('0777', 8) & (~process.umask()) }, function (err, tmpPath) {
|
||||
var file = fs.createWriteStream(path.join((this.path = tmpPath), 'index' + this.assetType));
|
||||
|
||||
req.get(src, function (res) {
|
||||
// If assetUrl results in a redirect we update the assetUrl to the redirect to url
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
template('action', { name: 'redirect detected', shizzle: this.assetUrl })
|
||||
.on('data', this.emit.bind(this, 'data'));
|
||||
this.assetUrl = res.headers.location;
|
||||
return this.download();
|
||||
}
|
||||
|
||||
// Detect not OK status codes
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
return this.emit('error', new Error(res.statusCode + ' status code for ' + this.assetUrl));
|
||||
}
|
||||
|
||||
res.on('data', function (data) {
|
||||
file.write(data);
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
file.end();
|
||||
|
||||
var next = function () {
|
||||
this.once('loadJSON', this.saveUnit).loadJSON();
|
||||
}.bind(this);
|
||||
|
||||
if (this.assetType === '.zip' || this.assetType === '.tar') this.once('extract', next).extract();
|
||||
else next();
|
||||
}.bind(this));
|
||||
|
||||
}.bind(this)).on('error', this.emit.bind(this, 'error'));
|
||||
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.extract = function () {
|
||||
var file = path.join(this.path, 'index' + this.assetType);
|
||||
template('action', { name: 'extracting', shizzle: file }).on('data', this.emit.bind(this, 'data'));
|
||||
|
||||
fs.createReadStream(file).pipe(this.assetType === '.zip' ? unzip.Extract({ path: this.path }) : tar.Extract({ path: this.path }))
|
||||
.on('error', this.emit.bind(this, 'error'))
|
||||
.on('end', function () {
|
||||
// Delete zip
|
||||
fs.unlink(file, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
// If we extracted only a folder, move all the files within it to the original path
|
||||
fs.readdir(this.path, function (err, files) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
if (files.length !== 1) return this.emit('extract');
|
||||
|
||||
var dir = path.join(this.path, files[0]);
|
||||
fs.stat(dir, function (err, stat) {
|
||||
if (err) return this.emit('error', err);
|
||||
if (!stat.isDirectory()) return this.emit('extract');
|
||||
|
||||
fs.readdir(dir, function (err, files) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
async.forEachSeries(files, function (file, next) {
|
||||
fs.rename(path.join(dir, file), path.join(this.path, file), next);
|
||||
}.bind(this), function (err) {
|
||||
if (err) return this.emit('error');
|
||||
|
||||
fs.rmdir(dir, function (err) {
|
||||
if (err) return this.emit('error');
|
||||
this.emit('extract');
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.copy = function () {
|
||||
template('action', { name: 'copying', shizzle: this.path }).on('data', this.emit.bind(this, 'data'));
|
||||
|
||||
tmp.dir({ prefix: 'bower-' + this.name + '-' }, function (err, tmpPath) {
|
||||
fs.stat(this.path, function (err, stats) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
// Copy file permission for directory
|
||||
fs.chmod(tmpPath, stats.mode, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
if (this.assetType) {
|
||||
return fs.readFile(this.path, function (err, data) {
|
||||
fs.writeFile(path.join((this.path = tmpPath), 'index' + this.assetType), data, function () {
|
||||
this.once('loadJSON', this.saveUnit).loadJSON();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.once('loadJSON', function () {
|
||||
if (this.gitUrl) return this.saveUnit();
|
||||
|
||||
// Check if the copied directory is a git repository and is a local endpoint
|
||||
// If so, treat it like a repository.
|
||||
fileExists(path.join(this.path, '.git'), function (exists) {
|
||||
if (!exists) return this.saveUnit();
|
||||
|
||||
this.gitPath = this.path;
|
||||
this.once('loadJSON', this.saveUnit.bind(this)).checkout();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
var writter = fstream.Writer({
|
||||
type: 'Directory',
|
||||
path: tmpPath
|
||||
})
|
||||
.on('error', this.emit.bind(this, 'error'))
|
||||
.on('end', this.loadJSON.bind(this));
|
||||
|
||||
fstream.Reader(this.path)
|
||||
.on('error', this.emit.bind(this, 'error'))
|
||||
.pipe(writter);
|
||||
|
||||
this.path = tmpPath;
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.getDeepDependencies = function (result) {
|
||||
result = result || [];
|
||||
for (var name in this.dependencies) {
|
||||
result.push(this.dependencies[name]);
|
||||
this.dependencies[name].getDeepDependencies(result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Package.prototype.saveUnit = function () {
|
||||
this.unitWork.store(this.name, this.serialize(), this);
|
||||
this.unitWork.unlock(this.name, this);
|
||||
this.addDependencies();
|
||||
};
|
||||
|
||||
Package.prototype.addDependencies = function () {
|
||||
var dependencies = this.json.dependencies || {};
|
||||
var callbacks = Object.keys(dependencies).map(function (name) {
|
||||
return function (callback) {
|
||||
var endpoint = dependencies[name];
|
||||
this.dependencies[name] = new Package(name, endpoint, this);
|
||||
this.dependencies[name].once('resolve', callback).resolve();
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
async.parallel(callbacks, this.emit.bind(this, 'resolve'));
|
||||
};
|
||||
|
||||
Package.prototype.exists = function (callback) {
|
||||
fileExists(this.localPath, callback);
|
||||
};
|
||||
|
||||
Package.prototype.clone = function () {
|
||||
template('action', { name: 'cloning', shizzle: this.gitUrl }).on('data', this.emit.bind(this, 'data'));
|
||||
this.path = this.gitPath;
|
||||
this.once('cache', function () {
|
||||
this.once('loadJSON', this.copy.bind(this)).checkout();
|
||||
}.bind(this)).cache();
|
||||
};
|
||||
|
||||
Package.prototype.cache = function () {
|
||||
// If the force options is true, we need to erase from the cache
|
||||
// Be aware that a similar package might already flushed it
|
||||
// To prevent that we check the unit of work storage
|
||||
if (this.opts.force && !this.unitWork.retrieve('flushed#' + this.name + '_' + this.resourceId)) {
|
||||
rimraf(this.path, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
this.unitWork.store('flushed#' + this.name + '_' + this.resourceId, true);
|
||||
this.cache();
|
||||
}.bind(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
mkdirp(config.cache, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
fileExists(this.path, function (exists) {
|
||||
if (exists) {
|
||||
template('action', { name: 'cached', shizzle: this.gitUrl }).on('data', this.emit.bind(this, 'data'));
|
||||
return this.emit('cache');
|
||||
}
|
||||
template('action', { name: 'caching', shizzle: this.gitUrl }).on('data', this.emit.bind(this, 'data'));
|
||||
var url = this.gitUrl;
|
||||
if (process.env.HTTP_PROXY) {
|
||||
url = url.replace(/^git:/, 'https:');
|
||||
}
|
||||
|
||||
mkdirp(this.path, function (err) {
|
||||
if (err) return this.emit('error', err);
|
||||
|
||||
var cp = spawn('git', ['clone', url, this.path], null, this);
|
||||
cp.on('close', function () {
|
||||
this.emit('cache');
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.checkout = function () {
|
||||
template('action', { name: 'fetching', shizzle: this.name })
|
||||
.on('data', this.emit.bind(this, 'data'));
|
||||
|
||||
this.once('versions', function (versions) {
|
||||
if (!versions.length) {
|
||||
this.emit('checkout');
|
||||
this.loadJSON();
|
||||
}
|
||||
|
||||
// If tag is specified, try to satisfy it
|
||||
if (this.tag) {
|
||||
versions = versions.filter(function (version) {
|
||||
return semver.satisfies(version, this.tag);
|
||||
}.bind(this));
|
||||
|
||||
if (!versions.length) {
|
||||
var error = new Error('Could not find tag satisfying: ' + this.name + '#' + this.tag);
|
||||
error.details = 'The tag ' + this.tag + ' could not be found within the repository';
|
||||
return this.emit('error', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Use latest version
|
||||
this.tag = versions[0];
|
||||
if (!semver.valid(this.tag)) this.commit = this.tag; // If the version is not valid, then its a commit
|
||||
|
||||
if (this.tag) {
|
||||
template('action', {
|
||||
name: 'checking out',
|
||||
shizzle: this.name + '#' + this.tag
|
||||
}).on('data', this.emit.bind(this, 'data'));
|
||||
|
||||
// Checkout the tag
|
||||
spawn('git', [ 'checkout', this.tag, '-f'], { cwd: this.path }, this).on('close', function () {
|
||||
// Ensure that checkout the tag as it is, removing all untracked files
|
||||
spawn('git', ['clean', '-f', '-d'], { cwd: this.path }, this).on('close', function () {
|
||||
this.emit('checkout');
|
||||
this.loadJSON();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
}).versions();
|
||||
};
|
||||
|
||||
Package.prototype.describeTag = function () {
|
||||
var cp = spawn('git', ['describe', '--always', '--tag'], { cwd: this.gitPath || this.path, ignoreCodes: [128] }, this);
|
||||
var tag = '';
|
||||
|
||||
cp.stdout.setEncoding('utf8');
|
||||
cp.stdout.on('data', function (data) {
|
||||
tag += data;
|
||||
});
|
||||
|
||||
cp.on('close', function (code) {
|
||||
if (code === 128) tag = 'unspecified'.grey; // Not a git repo
|
||||
this.emit('describeTag', tag.replace(/\n$/, ''));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.versions = function () {
|
||||
this.once('fetch', function () {
|
||||
var cp = spawn('git', ['tag'], { cwd: this.gitPath }, this);
|
||||
|
||||
var versions = '';
|
||||
|
||||
cp.stdout.setEncoding('utf8');
|
||||
cp.stdout.on('data', function (data) {
|
||||
versions += data;
|
||||
});
|
||||
|
||||
cp.on('close', function () {
|
||||
versions = versions.split('\n');
|
||||
versions = versions.filter(function (ver) {
|
||||
return semver.valid(ver);
|
||||
});
|
||||
versions = versions.sort(function (a, b) {
|
||||
return semver.gt(a, b) ? -1 : 1;
|
||||
});
|
||||
|
||||
if (versions.length) return this.emit('versions', versions);
|
||||
|
||||
// If there is no versions tagged in the repo
|
||||
// then we grab the hash of the last commit
|
||||
versions = '';
|
||||
cp = spawn('git', ['log', '-n', 1, '--format=%H'], { cwd: this.gitPath }, this);
|
||||
|
||||
cp.stdout.setEncoding('utf8');
|
||||
cp.stdout.on('data', function (data) {
|
||||
versions += data;
|
||||
});
|
||||
cp.on('close', function () {
|
||||
versions = versions.split('\n');
|
||||
this.emit('versions', versions);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this)).fetch();
|
||||
};
|
||||
|
||||
Package.prototype.fetch = function () {
|
||||
fileExists(this.gitPath, function (exists) {
|
||||
if (!exists) return this.emit('error', new Error('Unable to fetch package ' + this.name + ' (if the cache was deleted, run install again)'));
|
||||
|
||||
var cp = spawn('git', ['fetch', '--prune'], { cwd: this.gitPath }, this);
|
||||
cp.on('close', function () {
|
||||
cp = spawn('git', ['reset', '--hard', this.gitUrl ? 'origin/HEAD' : 'HEAD'], { cwd: this.gitPath }, this);
|
||||
cp.on('close', function () {
|
||||
this.emit('fetch');
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Package.prototype.fetchEndpoint = function () {
|
||||
var obj = this.readEndpoint(true);
|
||||
|
||||
if (obj) this.emit('fetchEndpoint', obj.endpoint, obj.type);
|
||||
else this.emit('fetchEndpoint');
|
||||
};
|
||||
|
||||
Package.prototype.readEndpoint = function (replace) {
|
||||
if (!this.json.repository) return;
|
||||
|
||||
if (this.json.repository.type === 'git') {
|
||||
if (replace || !this.gitUrl) {
|
||||
this.gitUrl = this.json.repository.url;
|
||||
this.generateResourceId();
|
||||
}
|
||||
return { type: 'git', endpoint: this.gitUrl };
|
||||
}
|
||||
if (this.json.repository.type === 'local-repo') {
|
||||
if (replace || !this.gitPath) {
|
||||
this.gitPath = path.resolve(this.json.repository.path);
|
||||
}
|
||||
return { type: 'local', endpoint: this.path };
|
||||
}
|
||||
if (this.json.repository.type === 'local') {
|
||||
if (replace || !this.path) {
|
||||
this.path = path.resolve(this.json.repository.path);
|
||||
}
|
||||
return { type: 'local', endpoint: this.path };
|
||||
}
|
||||
if (this.json.repository.type === 'asset') {
|
||||
if (replace || !this.assetUrl) {
|
||||
this.assetUrl = this.json.repository.url;
|
||||
this.assetType = path.extname(this.assetUrl);
|
||||
}
|
||||
return { type: 'asset', endpoint: this.assetUrl };
|
||||
}
|
||||
};
|
||||
|
||||
Package.prototype.waitUnlock = function (name) {
|
||||
if (this.name === name) {
|
||||
this.unitWork.removeListener('unlock', this.waitUnlock);
|
||||
this.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
Package.prototype.serialize = function () {
|
||||
return {
|
||||
id: this.id,
|
||||
resourceId: this.resourceId,
|
||||
path: this.path,
|
||||
originalPath: this.originalPath,
|
||||
tag: this.tag,
|
||||
originalTag: this.originalTag,
|
||||
commit: this.commit,
|
||||
assetUrl: this.assetUrl,
|
||||
assetType: this.assetType,
|
||||
lookedUp: this.lookedUp,
|
||||
json: this.json,
|
||||
gitUrl: this.gitUrl,
|
||||
gitPath: this.gitPath,
|
||||
dependencies: this.dependencies
|
||||
};
|
||||
};
|
||||
|
||||
Package.prototype.unserialize = function (obj) {
|
||||
for (var key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
|
||||
this.version = this.tag;
|
||||
};
|
||||
|
||||
Package.prototype.generateResourceId = function () {
|
||||
this.resourceId = crypto.createHash('md5').update(this.name + '%' + this.gitUrl).digest('hex');
|
||||
this.gitPath = path.join(config.cache, this.name, this.resourceId);
|
||||
};
|
||||
|
||||
Package.prototype.__defineGetter__('localPath', function () {
|
||||
return path.join(process.cwd(), config.directory, this.name);
|
||||
});
|
||||
|
||||
module.exports = Package;
|
||||
279
lib/core/resolverFactory.js
Normal file
279
lib/core/resolverFactory.js
Normal file
@@ -0,0 +1,279 @@
|
||||
var Q = require('q');
|
||||
var fs = require('../util/fs');
|
||||
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('../version');
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient).spread(function(
|
||||
ConcreteResolver,
|
||||
decEndpoint
|
||||
) {
|
||||
return new ConcreteResolver(
|
||||
decEndpoint,
|
||||
options.config,
|
||||
options.logger
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getConstructor(decEndpoint, options, registryClient) {
|
||||
var source = decEndpoint.source;
|
||||
var config = options.config;
|
||||
|
||||
// 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 constructor of resolver
|
||||
|
||||
var promise = Q.resolve();
|
||||
|
||||
var addResolver = function(resolverFactory) {
|
||||
promise = promise.then(function(result) {
|
||||
if (result === undefined) {
|
||||
return resolverFactory(decEndpoint, options);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Plugin resolvers.
|
||||
//
|
||||
// It requires each resolver defined in config.resolvers and calls
|
||||
// its "match" to check if given resolves supports given decEndpoint
|
||||
addResolver(function() {
|
||||
var selectedResolver;
|
||||
var resolverNames;
|
||||
|
||||
if (Array.isArray(config.resolvers)) {
|
||||
resolverNames = config.resolvers;
|
||||
} else if (!!config.resolvers) {
|
||||
resolverNames = config.resolvers.split(',');
|
||||
} else {
|
||||
resolverNames = [];
|
||||
}
|
||||
|
||||
var resolverPromises = resolverNames.map(function(resolverName) {
|
||||
var resolver = resolvers[resolverName];
|
||||
|
||||
if (resolver === undefined) {
|
||||
var resolverPath = resolve(resolverName, { cwd: config.cwd });
|
||||
|
||||
if (resolverPath === undefined) {
|
||||
throw createError(
|
||||
'Bower resolver not found: ' + resolverName,
|
||||
'ENORESOLVER'
|
||||
);
|
||||
}
|
||||
|
||||
resolver = pluginResolverFactory(
|
||||
require(resolverPath),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
return function() {
|
||||
if (selectedResolver === undefined) {
|
||||
var match = resolver.match.bind(resolver);
|
||||
|
||||
return Q.fcall(match, source).then(function(result) {
|
||||
if (result) {
|
||||
return (selectedResolver = resolver);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return selectedResolver;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
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)
|
||||
) {
|
||||
decEndpoint.source = source.replace(/^git\+/, '');
|
||||
|
||||
// If it's a GitHub repository, return the specialized resolver
|
||||
if (resolvers.GitHub.getOrgRepoPair(source)) {
|
||||
return [resolvers.GitHub, decEndpoint];
|
||||
}
|
||||
|
||||
return [resolvers.GitRemote, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
// SVN case: svn, svn+ssh, svn+http, svn+https, svn+file
|
||||
addResolver(function() {
|
||||
if (/^svn(\+(ssh|https?|file))?:\/\//i.test(source)) {
|
||||
return [resolvers.Svn, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
// URL case
|
||||
addResolver(function() {
|
||||
if (/^https?:\/\//i.exec(source)) {
|
||||
return [resolvers.Url, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
// If source is ./ or ../ or an absolute path
|
||||
|
||||
addResolver(function() {
|
||||
var absolutePath = path.resolve(config.cwd, 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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
return Q.resolve([resolvers.Fs, decEndpoint]);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if is a shorthand and expand it
|
||||
addResolver(function() {
|
||||
// Check if the shorthandResolver is falsy
|
||||
if (!config.shorthandResolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip ssh and/or URL with auth
|
||||
if (/[:@]/.test(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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]
|
||||
}
|
||||
);
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient);
|
||||
}
|
||||
});
|
||||
|
||||
// As last resort, we try the registry
|
||||
addResolver(function() {
|
||||
if (!registryClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Q.nfcall(
|
||||
registryClient.lookup.bind(registryClient),
|
||||
source
|
||||
).then(function(entry) {
|
||||
if (!entry) {
|
||||
throw createError(
|
||||
'Package ' + source + ' not found',
|
||||
'ENOTFOUND'
|
||||
);
|
||||
}
|
||||
|
||||
decEndpoint.registry = true;
|
||||
|
||||
if (!decEndpoint.name) {
|
||||
decEndpoint.name = decEndpoint.source;
|
||||
}
|
||||
|
||||
decEndpoint.source = entry.url;
|
||||
|
||||
return getConstructor(decEndpoint, options);
|
||||
});
|
||||
});
|
||||
|
||||
addResolver(function() {
|
||||
throw createError(
|
||||
'Could not find appropriate resolver for ' + source,
|
||||
'ENORESOLVER'
|
||||
);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function clearRuntimeCache() {
|
||||
mout.object.values(resolvers).forEach(function(ConcreteResolver) {
|
||||
ConcreteResolver.clearRuntimeCache();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = createInstance;
|
||||
module.exports.getConstructor = getConstructor;
|
||||
module.exports.clearRuntimeCache = clearRuntimeCache;
|
||||
154
lib/core/resolvers/FsResolver.js
Normal file
154
lib/core/resolvers/FsResolver.js
Normal file
@@ -0,0 +1,154 @@
|
||||
var util = require('util');
|
||||
var fs = require('../../util/fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var junk = require('junk');
|
||||
var Resolver = require('./Resolver');
|
||||
var copy = require('../../util/copy');
|
||||
var extract = require('../../util/extract');
|
||||
var createError = require('../../util/createError');
|
||||
|
||||
function FsResolver(decEndpoint, config, logger) {
|
||||
Resolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
// Ensure absolute path
|
||||
this._source = path.resolve(this._config.cwd, this._source);
|
||||
|
||||
// If target was specified, simply reject the promise
|
||||
if (this._target !== '*') {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(FsResolver, Resolver);
|
||||
mout.object.mixIn(FsResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
FsResolver.isTargetable = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
// TODO: Should we store latest mtimes in the resolution and compare?
|
||||
// This would be beneficial when copying big files/folders
|
||||
|
||||
// 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() {
|
||||
return this._copy()
|
||||
.then(this._extract.bind(this))
|
||||
.then(this._rename.bind(this));
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
FsResolver.prototype._copy = function() {
|
||||
var that = this;
|
||||
|
||||
return Q.nfcall(fs.stat, this._source).then(function(stat) {
|
||||
var dst;
|
||||
var copyOpts;
|
||||
var promise;
|
||||
|
||||
that._sourceStat = stat;
|
||||
copyOpts = { mode: stat.mode };
|
||||
|
||||
// If it's a folder
|
||||
if (stat.isDirectory()) {
|
||||
dst = that._tempDir;
|
||||
|
||||
// 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
|
||||
} else {
|
||||
dst = path.join(that._tempDir, path.basename(that._source));
|
||||
promise = copy
|
||||
.copyFile(that._source, dst, copyOpts)
|
||||
.then(function() {
|
||||
return dst;
|
||||
});
|
||||
}
|
||||
|
||||
that._logger.action('copy', that._source, {
|
||||
src: that._source,
|
||||
dst: dst
|
||||
});
|
||||
|
||||
return promise;
|
||||
});
|
||||
};
|
||||
|
||||
FsResolver.prototype._extract = function(file) {
|
||||
if (!file || !extract.canExtract(file)) {
|
||||
return Q.resolve();
|
||||
}
|
||||
|
||||
this._logger.action('extract', path.basename(this._source), {
|
||||
archive: file,
|
||||
to: this._tempDir
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
return Q.nfcall(fs.rename, oldPath, newPath);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
FsResolver.prototype._savePkgMeta = function(meta) {
|
||||
// Store main if is a single file
|
||||
if (this._singleFile) {
|
||||
meta.main = this._singleFile;
|
||||
}
|
||||
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
module.exports = FsResolver;
|
||||
102
lib/core/resolvers/GitFsResolver.js
Normal file
102
lib/core/resolvers/GitFsResolver.js
Normal file
@@ -0,0 +1,102 @@
|
||||
var util = require('util');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var path = require('path');
|
||||
var GitResolver = require('./GitResolver');
|
||||
var copy = require('../../util/copy');
|
||||
var cmd = require('../../util/cmd');
|
||||
|
||||
function GitFsResolver(decEndpoint, config, logger) {
|
||||
GitResolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
// Ensure absolute path
|
||||
this._source = path.resolve(this._config.cwd, this._source);
|
||||
}
|
||||
|
||||
util.inherits(GitFsResolver, GitResolver);
|
||||
mout.object.mixIn(GitFsResolver, GitResolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
// Override the checkout function to work with the local copy
|
||||
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
|
||||
}
|
||||
);
|
||||
|
||||
// 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
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
GitFsResolver.prototype._copy = function() {
|
||||
return copy.copyDir(this._source, this._tempDir);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Grab refs locally
|
||||
GitFsResolver.refs = function(source) {
|
||||
var value;
|
||||
|
||||
// TODO: Normalize source because of the various available protocols?
|
||||
value = this._cache.refs.get(source);
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
|
||||
return refs;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.refs.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
module.exports = GitFsResolver;
|
||||
191
lib/core/resolvers/GitHubResolver.js
Normal file
191
lib/core/resolvers/GitHubResolver.js
Normal file
@@ -0,0 +1,191 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var GitRemoteResolver = require('./GitRemoteResolver');
|
||||
var download = require('../../util/download');
|
||||
var extract = require('../../util/extract');
|
||||
var createError = require('../../util/createError');
|
||||
|
||||
function GitHubResolver(decEndpoint, config, logger) {
|
||||
var pair;
|
||||
|
||||
GitRemoteResolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
// Grab the org/repo
|
||||
// /xxxxx/yyyyy.git or :xxxxx/yyyyy.git (.git is optional)
|
||||
pair = GitHubResolver.getOrgRepoPair(this._source);
|
||||
if (!pair) {
|
||||
throw createError('Invalid GitHub URL', 'EINVEND', {
|
||||
details: this._source + ' does not seem to be a valid GitHub URL'
|
||||
});
|
||||
}
|
||||
|
||||
this._org = pair.org;
|
||||
this._repo = pair.repo;
|
||||
|
||||
// Ensure trailing for all protocols
|
||||
if (!mout.string.endsWith(this._source, '.git')) {
|
||||
this._source += '.git';
|
||||
}
|
||||
|
||||
// Use https:// rather than git:// if on a proxy
|
||||
if (this._config.proxy || this._config.httpsProxy) {
|
||||
this._source = this._source.replace('git://', 'https://');
|
||||
}
|
||||
|
||||
// Enable shallow clones for GitHub repos
|
||||
this._shallowClone = function() {
|
||||
return Q.resolve(true);
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(GitHubResolver, GitRemoteResolver);
|
||||
mout.object.mixIn(GitHubResolver, GitRemoteResolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitHubResolver.prototype._checkout = function() {
|
||||
var msg;
|
||||
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;
|
||||
|
||||
if (this._config.userAgent) {
|
||||
reqHeaders['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
this._logger.action('download', tarballUrl, {
|
||||
url: that._source,
|
||||
to: file
|
||||
});
|
||||
|
||||
// Download tarball
|
||||
return download(tarballUrl, file, {
|
||||
ca: this._config.ca.default,
|
||||
strictSSL: this._config.strictSsl,
|
||||
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
|
||||
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 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));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
GitHubResolver.prototype._savePkgMeta = function(meta) {
|
||||
// Set homepage if not defined
|
||||
if (!meta.homepage) {
|
||||
meta.homepage = 'https://github.com/' + this._org + '/' + this._repo;
|
||||
}
|
||||
|
||||
return GitRemoteResolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
// ----------------
|
||||
|
||||
GitHubResolver.getOrgRepoPair = function(url) {
|
||||
var match;
|
||||
|
||||
match = url.match(
|
||||
/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i
|
||||
);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
org: match[1],
|
||||
repo: match[2]
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = GitHubResolver;
|
||||
330
lib/core/resolvers/GitRemoteResolver.js
Normal file
330
lib/core/resolvers/GitRemoteResolver.js
Normal file
@@ -0,0 +1,330 @@
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var LRU = require('lru-cache');
|
||||
var GitResolver = require('./GitResolver');
|
||||
var cmd = require('../../util/cmd');
|
||||
|
||||
function GitRemoteResolver(decEndpoint, config, logger) {
|
||||
GitResolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
if (!mout.string.startsWith(this._source, 'file://')) {
|
||||
// Trim trailing slashes
|
||||
this._source = this._source.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
// If the name was guessed, remove the trailing .git
|
||||
if (this._guessedName && mout.string.endsWith(this._name, '.git')) {
|
||||
this._name = this._name.slice(0, -4);
|
||||
}
|
||||
|
||||
// Get the remote of this source
|
||||
if (!/:\/\//.test(this._source)) {
|
||||
this._remote = url.parse('ssh://' + this._source);
|
||||
} else {
|
||||
this._remote = url.parse(this._source);
|
||||
}
|
||||
|
||||
this._host = this._remote.host;
|
||||
|
||||
// Verify whether the server supports shallow cloning
|
||||
this._shallowClone = this._supportsShallowCloning;
|
||||
}
|
||||
|
||||
util.inherits(GitRemoteResolver, GitResolver);
|
||||
mout.object.mixIn(GitRemoteResolver, GitResolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
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
|
||||
}
|
||||
);
|
||||
|
||||
// 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
|
||||
} else {
|
||||
promise = this._fastClone(resolution);
|
||||
}
|
||||
|
||||
// Throttle the progress reporter to 1 time each sec
|
||||
reporter = mout.fn.throttle(function(data) {
|
||||
var lines;
|
||||
|
||||
lines = data.split(/[\r\n]+/);
|
||||
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?
|
||||
that._logger.info('progress', line.trim());
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// Start reporting progress after a few seconds
|
||||
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();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
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) {
|
||||
var branch,
|
||||
args,
|
||||
that = this;
|
||||
|
||||
branch = resolution.tag || resolution.branch;
|
||||
args = ['clone', this._source, '-b', branch, '--progress', '.'];
|
||||
|
||||
return this._shallowClone().then(function(shallowCloningSupported) {
|
||||
// If the host does not support shallow clones, we don't use --depth=1
|
||||
if (
|
||||
shallowCloningSupported &&
|
||||
!GitRemoteResolver._noShallow.get(that._host)
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
GitRemoteResolver.prototype._suggestProxyWorkaround = function(err) {
|
||||
if (
|
||||
(this._config.proxy || this._config.httpsProxy) &&
|
||||
mout.string.startsWith(this._source, 'git://') &&
|
||||
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 += '\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.';
|
||||
}
|
||||
};
|
||||
|
||||
// Verifies whether the server supports shallow cloning.
|
||||
// This is done according to the rules found in the following links:
|
||||
// * https://github.com/dimitri/el-get/pull/1921/files
|
||||
// * http://stackoverflow.com/questions/9270488/is-it-possible-to-detect-whether-a-http-git-remote-is-smart-or-dumb
|
||||
//
|
||||
// Summary of the rules:
|
||||
// * Protocols like ssh or git always support shallow cloning
|
||||
// * HTTP-based protocols can be verified by sending a HEAD or GET request to the URI (appended to the URL of the Git repo):
|
||||
// /info/refs?service=git-upload-pack
|
||||
// * If the server responds with a 'Content-Type' header of 'application/x-git-upload-pack-advertisement',
|
||||
// the server supports shallow cloning ("smart server")
|
||||
// * If the server responds with a different content type, the server does not support shallow cloning ("dumb server")
|
||||
// * Instead of doing the HEAD or GET request using an HTTP client, we're letting Git and Curl do the heavy lifting.
|
||||
// Calling Git with the GIT_CURL_VERBOSE=2 env variable will provide the Git and Curl output, which includes
|
||||
// the content type. This has the advantage that Git will take care of using stored credentials and any additional
|
||||
// negotiation that needs to take place.
|
||||
//
|
||||
// The above should cover most cases, including BitBucket.
|
||||
GitRemoteResolver.prototype._supportsShallowCloning = function() {
|
||||
var value = true;
|
||||
|
||||
// Verify that the remote could be parsed and that a protocol is set
|
||||
// This case is unlikely, but let's still cover it.
|
||||
if (this._remote == null || this._remote.protocol == null) {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
||||
if (
|
||||
!this._host ||
|
||||
!this._config.shallowCloneHosts ||
|
||||
this._config.shallowCloneHosts.indexOf(this._host) === -1
|
||||
) {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
||||
// Check for protocol - the remote check for hosts supporting shallow cloning is only required for
|
||||
// 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)
|
||||
) {
|
||||
// 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'
|
||||
});
|
||||
|
||||
value = cmd('git', ['ls-remote', '--heads', this._source], {
|
||||
env: processEnv
|
||||
}).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'
|
||||
);
|
||||
|
||||
this._logger.debug(
|
||||
'detect-smart-git',
|
||||
'Smart Git host detected: ' + isSmartServer
|
||||
);
|
||||
|
||||
if (isSmartServer) {
|
||||
// Cache this host
|
||||
GitRemoteResolver._canShallow.set(this._host, true);
|
||||
}
|
||||
|
||||
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
|
||||
return Q.resolve(true);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
// Grab refs remotely
|
||||
GitRemoteResolver.refs = function(source) {
|
||||
var value;
|
||||
|
||||
// TODO: Normalize source because of the various available protocols?
|
||||
value = this._cache.refs.get(source);
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
// Store the promise in the refs object
|
||||
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
|
||||
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
|
||||
return refs;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.refs.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
// Store hosts that do not support shallow clones here
|
||||
GitRemoteResolver._noShallow = new LRU({ max: 50, maxAge: 5 * 60 * 1000 });
|
||||
|
||||
// Store hosts that support shallow clones here
|
||||
GitRemoteResolver._canShallow = new LRU({ max: 50, maxAge: 5 * 60 * 1000 });
|
||||
|
||||
module.exports = GitRemoteResolver;
|
||||
434
lib/core/resolvers/GitResolver.js
Normal file
434
lib/core/resolvers/GitResolver.js
Normal file
@@ -0,0 +1,434 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var rimraf = require('../../util/rimraf');
|
||||
var mkdirp = require('mkdirp');
|
||||
var which = require('which');
|
||||
var LRU = require('lru-cache');
|
||||
var mout = require('mout');
|
||||
var Resolver = require('./Resolver');
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
|
||||
var hasGit;
|
||||
|
||||
// Check if git is installed
|
||||
try {
|
||||
which.sync('git');
|
||||
hasGit = true;
|
||||
} catch (ex) {
|
||||
hasGit = false;
|
||||
}
|
||||
|
||||
function GitResolver(decEndpoint, config, logger) {
|
||||
// Set template dir to the empty directory so that user templates are not run
|
||||
// This environment variable is not multiple config aware but it's not documented
|
||||
// anyway
|
||||
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) {
|
||||
throw createError('git is not installed or not in the PATH', 'ENOGIT');
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(GitResolver, Resolver);
|
||||
mout.object.mixIn(GitResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldResolution = pkgMeta._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)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// As last check, we compare both commit hashes
|
||||
return resolution.commit !== oldResolution.commit;
|
||||
});
|
||||
};
|
||||
|
||||
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();
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Abstract functions that should be implemented by concrete git resolvers
|
||||
GitResolver.prototype._checkout = function() {
|
||||
throw new Error('_checkout not implemented');
|
||||
};
|
||||
|
||||
GitResolver.refs = function(source) {
|
||||
throw new Error('refs not implemented');
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._findResolution = function(target) {
|
||||
var err;
|
||||
var self = this.constructor;
|
||||
var that = this;
|
||||
|
||||
target = target || this._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)) {
|
||||
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;
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on master
|
||||
if (!versions.length && target === '*') {
|
||||
return that._findResolution('master');
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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]
|
||||
});
|
||||
}
|
||||
|
||||
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'
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
GitResolver.prototype._cleanup = function() {
|
||||
var gitFolder = path.join(this._tempDir, '.git');
|
||||
|
||||
return Q.nfcall(rimraf, gitFolder);
|
||||
};
|
||||
|
||||
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.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
|
||||
meta.version = version;
|
||||
} else {
|
||||
// If resolved to a target that is not a version,
|
||||
// remove the version from the meta
|
||||
delete meta.version;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Save resolution to be used in hasNew later
|
||||
meta._resolution = this._resolution;
|
||||
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
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);
|
||||
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
|
||||
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]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.versions.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
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 = {};
|
||||
|
||||
// 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];
|
||||
}
|
||||
});
|
||||
|
||||
this._cache.tags.set(source, tags);
|
||||
|
||||
return tags;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.tags.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
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 = {};
|
||||
|
||||
// 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];
|
||||
}
|
||||
});
|
||||
|
||||
this._cache.branches.set(source, branches);
|
||||
|
||||
return branches;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.branches.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.clearRuntimeCache = function() {
|
||||
// Reset cache for branches, tags, etc
|
||||
mout.object.forOwn(GitResolver._cache, function(lru) {
|
||||
lru.reset();
|
||||
});
|
||||
};
|
||||
|
||||
GitResolver._cache = {
|
||||
branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
refs: new LRU({ max: 50, maxAge: 5 * 60 * 1000 })
|
||||
};
|
||||
|
||||
module.exports = GitResolver;
|
||||
252
lib/core/resolvers/Resolver.js
Normal file
252
lib/core/resolvers/Resolver.js
Normal file
@@ -0,0 +1,252 @@
|
||||
var fs = require('../../util/fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var tmp = require('tmp');
|
||||
var mkdirp = require('mkdirp');
|
||||
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();
|
||||
|
||||
function Resolver(decEndpoint, config, logger) {
|
||||
this._source = decEndpoint.source;
|
||||
this._target = decEndpoint.target || '*';
|
||||
this._name = decEndpoint.name || path.basename(this._source);
|
||||
|
||||
this._config = config;
|
||||
this._logger = logger;
|
||||
|
||||
this._guessedName = !decEndpoint.name;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
Resolver.prototype.getSource = function() {
|
||||
return this._source;
|
||||
};
|
||||
|
||||
Resolver.prototype.getName = function() {
|
||||
return this._name;
|
||||
};
|
||||
|
||||
Resolver.prototype.getTarget = function() {
|
||||
return this._target;
|
||||
};
|
||||
|
||||
Resolver.prototype.getTempDir = function() {
|
||||
return this._tempDir;
|
||||
};
|
||||
|
||||
Resolver.prototype.getPkgMeta = function() {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
Resolver.prototype.hasNew = function(pkgMeta) {
|
||||
var promise;
|
||||
var that = this;
|
||||
|
||||
// If already working, error out
|
||||
if (this._working) {
|
||||
return Q.reject(createError('Already working', 'EWORKING'));
|
||||
}
|
||||
|
||||
this._working = true;
|
||||
|
||||
// Avoid reading the package meta if already given
|
||||
promise = this._hasNew(pkgMeta);
|
||||
|
||||
return promise.fin(function() {
|
||||
that._working = false;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype.resolve = function() {
|
||||
var that = this;
|
||||
|
||||
// If already working, error out
|
||||
if (this._working) {
|
||||
return Q.reject(createError('Already working', 'EWORKING'));
|
||||
}
|
||||
|
||||
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;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
Resolver.prototype.isCacheable = function() {
|
||||
// Bypass cache for local dependencies
|
||||
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 &&
|
||||
this._pkgMeta._resolution &&
|
||||
this._pkgMeta._resolution.type === 'branch'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Abstract functions that must be implemented by concrete resolvers
|
||||
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) {
|
||||
return Q.resolve(true);
|
||||
};
|
||||
|
||||
Resolver.isTargetable = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
Resolver.versions = function(source) {
|
||||
return Q.resolve([]);
|
||||
};
|
||||
|
||||
Resolver.clearRuntimeCache = function() {};
|
||||
|
||||
// -----------------
|
||||
|
||||
Resolver.prototype._createTempDir = function() {
|
||||
return Q.nfcall(mkdirp, this._config.tmp)
|
||||
.then(
|
||||
function() {
|
||||
return Q.nfcall(tmp.dir, {
|
||||
template: path.join(
|
||||
this._config.tmp,
|
||||
md5(this._name) + '-' + process.pid + '-XXXXXX'
|
||||
),
|
||||
mode: 0777 & ~process.umask(),
|
||||
unsafeCleanup: true
|
||||
});
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function(dir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return (this._tempDir = Array.isArray(dir) ? dir[0] : dir);
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
Resolver.prototype._cleanTempDir = function() {
|
||||
var tempDir = this._tempDir;
|
||||
|
||||
if (!tempDir) {
|
||||
return Q.resolve();
|
||||
}
|
||||
|
||||
// Delete and create folder
|
||||
return Q.nfcall(rimraf, tempDir)
|
||||
.then(function() {
|
||||
return Q.nfcall(mkdirp, tempDir, 0777 & ~process.umask());
|
||||
})
|
||||
.then(function() {
|
||||
return tempDir;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype._readJson = function(dir) {
|
||||
var that = this;
|
||||
|
||||
dir = dir || this._tempDir;
|
||||
return readJson(dir, {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
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) {
|
||||
this._name = meta.name;
|
||||
}
|
||||
|
||||
// Handle ignore property, deleting all files from the temporary directory
|
||||
// If no ignores were specified, simply resolve
|
||||
if (!meta.ignore || !meta.ignore.length) {
|
||||
return Q.resolve(meta);
|
||||
}
|
||||
|
||||
// Otherwise remove them from the temp dir
|
||||
return removeIgnores(this._tempDir, meta).then(function() {
|
||||
return meta;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype._savePkgMeta = function(meta) {
|
||||
var that = this;
|
||||
var contents;
|
||||
|
||||
// Store original source & target
|
||||
meta._source = this._source;
|
||||
meta._target = this._target;
|
||||
|
||||
// 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);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Resolver;
|
||||
491
lib/core/resolvers/SvnResolver.js
Normal file
491
lib/core/resolvers/SvnResolver.js
Normal file
@@ -0,0 +1,491 @@
|
||||
var util = require('util');
|
||||
var Q = require('q');
|
||||
var which = require('which');
|
||||
var LRU = require('lru-cache');
|
||||
var mout = require('mout');
|
||||
var Resolver = require('./Resolver');
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
var cmd = require('../../util/cmd');
|
||||
|
||||
var hasSvn;
|
||||
|
||||
// Check if svn is installed
|
||||
try {
|
||||
which.sync('svn');
|
||||
hasSvn = true;
|
||||
} catch (ex) {
|
||||
hasSvn = false;
|
||||
}
|
||||
|
||||
function SvnResolver(decEndpoint, config, logger) {
|
||||
Resolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
if (!hasSvn) {
|
||||
throw createError('svn is not installed or not in the PATH', 'ENOSVN');
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(SvnResolver, Resolver);
|
||||
mout.object.mixIn(SvnResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
SvnResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldResolution = pkgMeta._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)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// As last check, we compare both commit hashes
|
||||
return resolution.commit !== oldResolution.commit;
|
||||
});
|
||||
};
|
||||
|
||||
SvnResolver.prototype._resolve = function() {
|
||||
var that = this;
|
||||
|
||||
return this._findResolution().then(function() {
|
||||
return that._export();
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._export = function() {
|
||||
var promise;
|
||||
var timer;
|
||||
var reporter;
|
||||
var that = this;
|
||||
var resolution = this._resolution;
|
||||
|
||||
this.source = SvnResolver.getSource(this._source);
|
||||
|
||||
this._logger.action(
|
||||
'export',
|
||||
resolution.tag || resolution.branch || resolution.commit,
|
||||
{
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
}
|
||||
);
|
||||
|
||||
if (resolution.type === 'commit') {
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/trunk',
|
||||
'-r' + resolution.commit,
|
||||
this._tempDir
|
||||
]);
|
||||
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/trunk',
|
||||
this._tempDir
|
||||
]);
|
||||
} else if (resolution.type === 'branch') {
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/branches/' + resolution.branch,
|
||||
this._tempDir
|
||||
]);
|
||||
} else {
|
||||
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) {
|
||||
var lines;
|
||||
|
||||
lines = data.split(/[\r\n]+/);
|
||||
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?
|
||||
that._logger.info('progress', line.trim());
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// Start reporting progress after a few seconds
|
||||
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();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._findResolution = function(target) {
|
||||
var err;
|
||||
var self = this.constructor;
|
||||
var that = this;
|
||||
|
||||
target = target || this._target || '*';
|
||||
|
||||
this._source = SvnResolver.getSource(this._source);
|
||||
|
||||
// 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)) {
|
||||
target = target.split('r');
|
||||
|
||||
this._resolution = { type: 'commit', commit: target[1] };
|
||||
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;
|
||||
});
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on trunk
|
||||
if (!versions.length && target === '*') {
|
||||
return that._findResolution('trunk');
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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]
|
||||
});
|
||||
}
|
||||
|
||||
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]
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure package meta version is the same as the resolution
|
||||
meta.version = version;
|
||||
} else {
|
||||
// If resolved to a target that is not a version,
|
||||
// remove the version from the meta
|
||||
delete meta.version;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Save resolution to be used in hasNew later
|
||||
meta._resolution = this._resolution;
|
||||
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
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);
|
||||
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
|
||||
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]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.versions.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.tags = function(source) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.tags.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.tags.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.branches = function(source) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.branches.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', [
|
||||
'list',
|
||||
source + '/branches',
|
||||
'--verbose',
|
||||
'--non-interactive'
|
||||
]).spread(
|
||||
function(stout) {
|
||||
var branches = SvnResolver.parseSubversionListOutput(
|
||||
stout.toString()
|
||||
);
|
||||
|
||||
// trunk is a branch!
|
||||
branches.trunk = '*';
|
||||
|
||||
this._cache.branches.set(source, branches);
|
||||
return branches;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.branches.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.parseSubversionListOutput = function(stout) {
|
||||
var entries = {};
|
||||
var lines = stout.trim().split(/[\r\n]+/);
|
||||
|
||||
// For each line in the refs, match only the branches
|
||||
lines.forEach(function(line) {
|
||||
var match = line.match(/\s+([0-9]+)\s.+\s([\w.$-]+)\//i);
|
||||
|
||||
if (match && match[2] !== '.') {
|
||||
entries[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
|
||||
return entries;
|
||||
};
|
||||
|
||||
SvnResolver.clearRuntimeCache = function() {
|
||||
// Reset cache for branches, tags, etc
|
||||
mout.object.forOwn(SvnResolver._cache, function(lru) {
|
||||
lru.reset();
|
||||
});
|
||||
};
|
||||
|
||||
SvnResolver._cache = {
|
||||
branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 })
|
||||
};
|
||||
|
||||
module.exports = SvnResolver;
|
||||
323
lib/core/resolvers/UrlResolver.js
Normal file
323
lib/core/resolvers/UrlResolver.js
Normal file
@@ -0,0 +1,323 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var fs = require('../../util/fs');
|
||||
var url = require('url');
|
||||
var request = require('request');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var junk = require('junk');
|
||||
var Resolver = require('./Resolver');
|
||||
var download = require('../../util/download');
|
||||
var extract = require('../../util/extract');
|
||||
var createError = require('../../util/createError');
|
||||
|
||||
function UrlResolver(decEndpoint, config, logger) {
|
||||
Resolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
// If target was specified, error out
|
||||
if (this._target !== '*') {
|
||||
throw createError("URL sources can't resolve targets", 'ENORESTARGET');
|
||||
}
|
||||
|
||||
// If the name was guessed
|
||||
if (this._guessedName) {
|
||||
// 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._remote = url.parse(this._source);
|
||||
}
|
||||
|
||||
util.inherits(UrlResolver, Resolver);
|
||||
mout.object.mixIn(UrlResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
UrlResolver.isTargetable = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
UrlResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldCacheHeaders = pkgMeta._cacheHeaders || {};
|
||||
var reqHeaders = {};
|
||||
|
||||
// If the previous cache headers contain an ETag,
|
||||
// send the "If-None-Match" header with it
|
||||
if (oldCacheHeaders.ETag) {
|
||||
reqHeaders['If-None-Match'] = oldCacheHeaders.ETag;
|
||||
}
|
||||
|
||||
if (this._config.userAgent) {
|
||||
reqHeaders['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// 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))
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
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;
|
||||
|
||||
if (this._config.userAgent) {
|
||||
reqHeaders['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
this._logger.action('download', that._source, {
|
||||
url: that._source,
|
||||
to: file
|
||||
});
|
||||
|
||||
// Download the file
|
||||
return download(this._source, file, {
|
||||
ca: this._config.ca.default,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
})
|
||||
.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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var disposition;
|
||||
var newFile;
|
||||
var match;
|
||||
|
||||
// Check if we got a Content-Disposition header
|
||||
disposition = response.headers['content-disposition'];
|
||||
if (!disposition) {
|
||||
return Q.resolve([file, response]);
|
||||
}
|
||||
|
||||
// Since there's various security issues with parsing this header, we only
|
||||
// interpret word chars plus dots, dashes and spaces
|
||||
match = disposition.match(/filename=(?:"([\w\-\. ]+)")/i);
|
||||
if (!match) {
|
||||
// The spec defines that the filename must be in quotes,
|
||||
// though a wide range of servers do not follow the rule
|
||||
match = disposition.match(/filename=([\w\-\.]+)/i);
|
||||
if (!match) {
|
||||
return Q.resolve([file, response]);
|
||||
}
|
||||
}
|
||||
|
||||
// Trim spaces
|
||||
newFile = match[1].trim();
|
||||
|
||||
// The filename can't end with a dot because this is known
|
||||
// to cause issues in Windows
|
||||
// See: http://superuser.com/questions/230385/dots-at-end-of-file-name
|
||||
if (mout.string.endsWith(newFile, '.')) {
|
||||
return Q.resolve([file, response]);
|
||||
}
|
||||
|
||||
newFile = path.join(this._tempDir, newFile);
|
||||
|
||||
return Q.nfcall(fs.rename, file, newFile).then(function() {
|
||||
return [newFile, 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, ['"', "'"]);
|
||||
}
|
||||
|
||||
if (!extract.canExtract(file, mimeType)) {
|
||||
return Q.resolve();
|
||||
}
|
||||
|
||||
this._logger.action('extract', path.basename(this._source), {
|
||||
archive: file,
|
||||
to: this._tempDir
|
||||
});
|
||||
|
||||
return extract(file, this._tempDir, {
|
||||
mimeType: mimeType
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
// 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)
|
||||
);
|
||||
};
|
||||
|
||||
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), '"');
|
||||
}
|
||||
|
||||
// Store main if is a single file
|
||||
if (this._singleFile) {
|
||||
meta.main = this._singleFile;
|
||||
}
|
||||
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
UrlResolver.prototype._collectCacheHeaders = function(res) {
|
||||
var headers = {};
|
||||
|
||||
// Collect cache headers
|
||||
this.constructor._cacheHeaders.forEach(function(name) {
|
||||
var value = res.headers[name.toLowerCase()];
|
||||
|
||||
if (value != null) {
|
||||
headers[name] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
UrlResolver._cacheHeaders = [
|
||||
'Content-MD5',
|
||||
'ETag',
|
||||
'Last-Modified',
|
||||
'Content-Language',
|
||||
'Content-Length',
|
||||
'Content-Type',
|
||||
'Content-Disposition'
|
||||
];
|
||||
|
||||
module.exports = UrlResolver;
|
||||
8
lib/core/resolvers/index.js
Normal file
8
lib/core/resolvers/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
GitFs: require('./GitFsResolver'),
|
||||
GitRemote: require('./GitRemoteResolver'),
|
||||
GitHub: require('./GitHubResolver'),
|
||||
Svn: require('./SvnResolver'),
|
||||
Fs: require('./FsResolver'),
|
||||
Url: require('./UrlResolver')
|
||||
};
|
||||
385
lib/core/resolvers/pluginResolverFactory.js
Normal file
385
lib/core/resolvers/pluginResolverFactory.js
Normal file
@@ -0,0 +1,385 @@
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var fs = require('../../util/fs');
|
||||
var object = require('mout/object');
|
||||
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
var readJson = require('../../util/readJson');
|
||||
var removeIgnores = require('../../util/removeIgnores');
|
||||
|
||||
function pluginResolverFactory(resolverFactory, bower) {
|
||||
bower = bower || {};
|
||||
|
||||
if (typeof resolverFactory !== 'function') {
|
||||
throw createError(
|
||||
'Resolver has "' +
|
||||
typeof resolverFactory +
|
||||
'" type instead of "function" type.',
|
||||
'ERESOLERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
var resolver = resolverFactory(bower);
|
||||
|
||||
function maxSatisfyingVersion(versions, target) {
|
||||
var versionsArr, index;
|
||||
|
||||
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) {
|
||||
return versions[index];
|
||||
}
|
||||
}
|
||||
|
||||
function PluginResolver(decEndpoint) {
|
||||
this._decEndpoint = decEndpoint;
|
||||
}
|
||||
|
||||
// @private
|
||||
PluginResolver.prototype.getEndpoint = function() {
|
||||
return object.merge(this._decEndpoint, {
|
||||
name: this.getName(),
|
||||
source: this.getSource(),
|
||||
target: this.getTarget()
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getSource = function() {
|
||||
return this._decEndpoint.source;
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getTarget = function() {
|
||||
return this._decEndpoint.target || '*';
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getName = function() {
|
||||
if (!this._decEndpoint.name && typeof resolver.getName === 'function') {
|
||||
return resolver.getName.call(resolver, this.getSource());
|
||||
} else if (!this._decEndpoint.name) {
|
||||
return path.basename(this.getSource());
|
||||
} else {
|
||||
return this._decEndpoint.name;
|
||||
}
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getPkgMeta = function() {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Plugin Resolver is always considered potentially cacheable
|
||||
// The "resolve" method decides whether to use cached or fetch new version.
|
||||
PluginResolver.prototype.isCacheable = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Not only it's always potentially cacheable, but also always potenially new.
|
||||
// The "resolve" handles logic of re-downloading target if needed.
|
||||
PluginResolver.prototype.hasNew = function(pkgMeta) {
|
||||
if (this.hasNewPromise) {
|
||||
return this.hasNewPromise;
|
||||
}
|
||||
|
||||
this._pkgMeta = pkgMeta;
|
||||
|
||||
return (this.hasNewPromise = this.resolve().then(function(result) {
|
||||
return result !== undefined;
|
||||
}));
|
||||
};
|
||||
|
||||
PluginResolver.prototype.resolve = function() {
|
||||
if (this.resolvePromise) {
|
||||
return this.resolvePromise;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
|
||||
return (this.resolvePromise = Q.fcall(function() {
|
||||
var target = that.getTarget();
|
||||
|
||||
// It means that we can accept ranges as targets
|
||||
if (that.constructor.isTargetable()) {
|
||||
that._release = target;
|
||||
|
||||
if (semver.validRange(target)) {
|
||||
return Q.fcall(
|
||||
resolver.releases.bind(resolver),
|
||||
that.getSource()
|
||||
).then(function(result) {
|
||||
if (!result) {
|
||||
throw createError(
|
||||
'Resolver did not provide releases of package.'
|
||||
);
|
||||
}
|
||||
|
||||
var releases = (that._releases = result);
|
||||
|
||||
var versions = releases.filter(function(target) {
|
||||
return semver.clean(target.version);
|
||||
});
|
||||
|
||||
var maxRelease = maxSatisfyingVersion(versions, target);
|
||||
|
||||
if (maxRelease) {
|
||||
that._version = maxRelease.version;
|
||||
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(', ')
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (semver.validRange(target) && 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 whether 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.'
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
var that = this;
|
||||
|
||||
return readJson(dir, {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
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) {
|
||||
this._name = meta.name;
|
||||
}
|
||||
|
||||
// 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
|
||||
) {
|
||||
return Q.resolve(meta);
|
||||
}
|
||||
|
||||
// Otherwise remove them from the temp dir
|
||||
return removeIgnores(this._tempDir, meta).then(function() {
|
||||
return meta;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype._savePkgMeta = function(meta, result) {
|
||||
var that = this;
|
||||
|
||||
meta._source = that.getSource();
|
||||
meta._target = that.getTarget();
|
||||
|
||||
if (result.resolution) {
|
||||
meta._resolution = result.resolution;
|
||||
}
|
||||
|
||||
if (that._release) {
|
||||
meta._release = that._release;
|
||||
}
|
||||
|
||||
if (that._version) {
|
||||
meta.version = that._version;
|
||||
} else {
|
||||
delete meta.version;
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
};
|
||||
|
||||
// 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
|
||||
) {
|
||||
if (!result) {
|
||||
throw createError(
|
||||
'Resolver did not provide releases of package.'
|
||||
);
|
||||
}
|
||||
|
||||
var releases = (this._releases = result);
|
||||
|
||||
var versions = releases.map(function(version) {
|
||||
return semver.clean(version.version);
|
||||
});
|
||||
|
||||
versions = versions.filter(function(version) {
|
||||
return version;
|
||||
});
|
||||
|
||||
versions.sort(function(a, b) {
|
||||
return semver.rcompare(a, b);
|
||||
});
|
||||
|
||||
return versions;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.isTargetable = function() {
|
||||
// If resolver doesn't define versions function, it's not targetable..
|
||||
return typeof resolver.releases === 'function';
|
||||
};
|
||||
|
||||
PluginResolver.clearRuntimeCache = function() {
|
||||
resolver = resolverFactory(bower);
|
||||
};
|
||||
|
||||
PluginResolver.match = function(source) {
|
||||
if (typeof resolver.match !== 'function') {
|
||||
throw createError(
|
||||
'Resolver is missing "match" method.',
|
||||
'ERESOLVERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
var match = resolver.match.bind(resolver);
|
||||
|
||||
return Q.fcall(match, source).then(function(result) {
|
||||
if (typeof result !== 'boolean') {
|
||||
throw createError(
|
||||
'Resolver\'s "match" method should return a boolean',
|
||||
'ERESOLVERAPI'
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.locate = function(source) {
|
||||
if (typeof resolver.locate !== 'function') {
|
||||
return source;
|
||||
}
|
||||
|
||||
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'
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
return PluginResolver;
|
||||
}
|
||||
|
||||
module.exports = pluginResolverFactory;
|
||||
122
lib/core/scripts.js
Normal file
122
lib/core/scripts.js
Normal file
@@ -0,0 +1,122 @@
|
||||
var mout = require('mout');
|
||||
var cmd = require('../util/cmd');
|
||||
var Q = require('q');
|
||||
var shellquote = require('shell-quote');
|
||||
|
||||
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 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)
|
||||
);
|
||||
|
||||
//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++) {
|
||||
var packageName = desiredOrder[i];
|
||||
if (depsSatisfied(packageName)) {
|
||||
ordered.push(packageName);
|
||||
mout.array.remove(desiredOrder, packageName);
|
||||
//as soon as we resolve a package start the loop again
|
||||
resolvedOne = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolvedOne && desiredOrder.length > 0) {
|
||||
//if we're here then some package(s) doesn't have all its deps satisified
|
||||
//so lets just jam those names on the end
|
||||
ordered = ordered.concat(desiredOrder);
|
||||
}
|
||||
}
|
||||
|
||||
return ordered;
|
||||
};
|
||||
|
||||
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 args = shellquote.parse(cmdString, env);
|
||||
var cmdName = args[0];
|
||||
mout.array.remove(args, cmdName); //no rest() in mout
|
||||
|
||||
var options = {
|
||||
cwd: config.cwd,
|
||||
env: env
|
||||
};
|
||||
|
||||
var promise = cmd(cmdName, args, options);
|
||||
|
||||
promise.progress(function(progress) {
|
||||
progress.split('\n').forEach(function(line) {
|
||||
if (line) {
|
||||
logger.action(action, line);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
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 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
|
||||
_orderByDependencies: orderByDependencies
|
||||
};
|
||||
@@ -1,72 +0,0 @@
|
||||
// ==========================================
|
||||
// BOWER: Source Api
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
|
||||
var request = require('request');
|
||||
var config = require('./config');
|
||||
|
||||
var endpoint = config.endpoint + '/packages';
|
||||
|
||||
if (process.env.HTTP_PROXY) {
|
||||
request = request.defaults({'proxy': process.env.HTTP_PROXY});
|
||||
}
|
||||
|
||||
exports.lookup = function (name, callback) {
|
||||
request.get(endpoint + '/' + encodeURIComponent(name), function (err, response, body) {
|
||||
if (err || response.statusCode !== 200) return callback(err || new Error(name + ' not found'));
|
||||
callback(err, body && JSON.parse(body).url);
|
||||
});
|
||||
};
|
||||
|
||||
exports.register = function (name, url, callback) {
|
||||
var body = {name: name, url: url};
|
||||
|
||||
request.post({url: endpoint, form: body}, function (err, response) {
|
||||
if (err) return callback(err);
|
||||
|
||||
if (response.statusCode === 406) {
|
||||
return callback(new Error('Duplicate package'));
|
||||
}
|
||||
|
||||
if (response.statusCode === 400) {
|
||||
return callback(new Error('Incorrect format'));
|
||||
}
|
||||
|
||||
if (response.statusCode !== 201) {
|
||||
return callback(new Error('Unknown error: ' + response.statusCode));
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
exports.search = function (name, callback) {
|
||||
request.get(endpoint + '/search/' + encodeURIComponent(name), function (err, response, body) {
|
||||
callback(err, body && JSON.parse(body));
|
||||
});
|
||||
};
|
||||
|
||||
exports.info = function (name, callback) {
|
||||
exports.lookup(name, function (err, url) {
|
||||
if (err) return callback(err);
|
||||
|
||||
var Package = require('./package');
|
||||
var pkg = new Package(name, url);
|
||||
|
||||
pkg.once('resolve', function () {
|
||||
pkg.once('versions', function (versions) {
|
||||
callback(null, { pkg: pkg, versions: versions });
|
||||
}).versions();
|
||||
}).resolve();
|
||||
});
|
||||
};
|
||||
|
||||
exports.all = function (callback) {
|
||||
request.get(endpoint, function (err, response, body) {
|
||||
callback(err, body && JSON.parse(body));
|
||||
});
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
// ==========================================
|
||||
// BOWER: Package Object Definition
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
// Events:
|
||||
// - lock: fired when a lock write over a key is acquired
|
||||
// - unlock: fired when an unlock write over a key is acquired
|
||||
// ==========================================
|
||||
|
||||
var events = require('events');
|
||||
|
||||
var UnitWork = function () {
|
||||
this.locks = [];
|
||||
this.data = [];
|
||||
|
||||
this.setMaxListeners(100); // Increase the number of listeners because this is a central storage
|
||||
};
|
||||
|
||||
UnitWork.prototype = Object.create(events.EventEmitter.prototype);
|
||||
|
||||
UnitWork.prototype.constructor = UnitWork;
|
||||
|
||||
UnitWork.prototype.lock = function (key, owner) {
|
||||
if (this.locks[key]) throw new Error('A write lock for "' + key + '" was already acquired.');
|
||||
if (!owner) throw new Error('A lock requires an owner.');
|
||||
this.locks[key] = owner;
|
||||
|
||||
return this.emit('lock', key);
|
||||
};
|
||||
|
||||
UnitWork.prototype.unlock = function (key, owner) {
|
||||
if (!owner) throw new Error('A write lock requires an owner.');
|
||||
if (this.locks[key]) {
|
||||
if (this.locks[key] !== owner) throw new Error('Lock owner for "' + key + '" mismatch.');
|
||||
delete this.locks[key];
|
||||
this.emit('unlock', key);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UnitWork.prototype.isLocked = function (key) {
|
||||
return !!this.locks[key];
|
||||
};
|
||||
|
||||
UnitWork.prototype.store = function (key, data, owner) {
|
||||
if (this.locks[key] && owner !== this.locks[key]) throw new Error('A write lock for "' + key + '" is acquired therefore only its owner can write to it.');
|
||||
this.data[key] = data;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UnitWork.prototype.retrieve = function (key) {
|
||||
return this.data[key];
|
||||
};
|
||||
|
||||
UnitWork.prototype.keys = function () {
|
||||
return Object.keys(this.data);
|
||||
};
|
||||
|
||||
module.exports = UnitWork;
|
||||
27
lib/index.js
27
lib/index.js
@@ -1,12 +1,19 @@
|
||||
// ==========================================
|
||||
// BOWER: Public API Defintion
|
||||
// ==========================================
|
||||
// Copyright 2012 Twitter, Inc
|
||||
// Licensed under The MIT License
|
||||
// http://opensource.org/licenses/MIT
|
||||
// ==========================================
|
||||
var commands = require('./commands');
|
||||
var version = require('./version');
|
||||
var abbreviations = require('./util/abbreviations')(commands);
|
||||
|
||||
function clearRuntimeCache() {
|
||||
// Note that in edge cases, some architecture components instance's
|
||||
// in-memory cache might be skipped.
|
||||
// If that's a problem, you should create and fresh instances instead.
|
||||
var PackageRepository = require('./core/PackageRepository');
|
||||
PackageRepository.clearRuntimeCache();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
commands: require('./commands'),
|
||||
config: require('./core/config')
|
||||
};
|
||||
version: version,
|
||||
commands: commands,
|
||||
config: require('./config')(),
|
||||
abbreviations: abbreviations,
|
||||
reset: clearRuntimeCache
|
||||
};
|
||||
|
||||
136
lib/renderers/JsonRenderer.js
Normal file
136
lib/renderers/JsonRenderer.js
Normal file
@@ -0,0 +1,136 @@
|
||||
var chalk = require('chalk');
|
||||
var Q = require('q');
|
||||
var promptly = require('promptly');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function JsonRenderer() {
|
||||
this._nrLogs = 0;
|
||||
}
|
||||
|
||||
JsonRenderer.prototype.end = function(data) {
|
||||
if (this._nrLogs) {
|
||||
process.stderr.write(']\n');
|
||||
}
|
||||
|
||||
if (data) {
|
||||
process.stdout.write(this._stringify(data) + '\n');
|
||||
}
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.error = function(err) {
|
||||
var message = err.message;
|
||||
var stack;
|
||||
|
||||
err.id = err.code || 'error';
|
||||
err.level = 'error';
|
||||
err.data = err.data || {};
|
||||
|
||||
// Need to set message again because it is
|
||||
// not enumerable in some cases
|
||||
delete err.message;
|
||||
err.message = message;
|
||||
|
||||
// Stack
|
||||
stack = err.fstream_stack || err.stack || 'N/A';
|
||||
err.stacktrace = Array.isArray(stack) ? stack.join('\n') : stack;
|
||||
|
||||
this.log(err);
|
||||
this.end();
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.log = function(log) {
|
||||
if (!this._nrLogs) {
|
||||
process.stderr.write('[');
|
||||
} else {
|
||||
process.stderr.write(', ');
|
||||
}
|
||||
|
||||
process.stderr.write(this._stringify(log));
|
||||
this._nrLogs++;
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.prompt = function(prompts) {
|
||||
var promise = Q.resolve();
|
||||
var answers = {};
|
||||
var that = this;
|
||||
|
||||
prompts.forEach(function(prompt) {
|
||||
var opts;
|
||||
var funcName;
|
||||
|
||||
// Strip colors
|
||||
prompt.message = chalk.stripColor(prompt.message);
|
||||
|
||||
// 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);
|
||||
|
||||
if (typeof ret === 'string') {
|
||||
throw ret;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// For now only "input", "confirm" and "password" are supported
|
||||
switch (prompt.type) {
|
||||
case 'input':
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
case 'confirm':
|
||||
case 'password':
|
||||
funcName = prompt.type;
|
||||
break;
|
||||
case 'checkbox':
|
||||
funcName = 'prompt';
|
||||
break;
|
||||
default:
|
||||
promise = promise.then(function() {
|
||||
throw createError('Unknown prompt type', 'ENOTSUP');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
promise = promise.then(function() {
|
||||
// Log
|
||||
prompt.level = 'prompt';
|
||||
that.log(prompt);
|
||||
|
||||
return Q.nfcall(promptly[funcName], '', opts).then(function(
|
||||
answer
|
||||
) {
|
||||
answers[prompt.name] = answer;
|
||||
});
|
||||
});
|
||||
|
||||
if (prompt.type === 'checkbox') {
|
||||
promise = promise.then(function() {
|
||||
answers[prompt.name] = answers[prompt.name].split(',');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return promise.then(function() {
|
||||
return answers;
|
||||
});
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
JsonRenderer.prototype._stringify = function(log) {
|
||||
// To json
|
||||
var str = JSON.stringify(log, null, ' ');
|
||||
// Remove colors in case some log has colors..
|
||||
str = chalk.stripColor(str);
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
module.exports = JsonRenderer;
|
||||
542
lib/renderers/StandardRenderer.js
Normal file
542
lib/renderers/StandardRenderer.js
Normal file
@@ -0,0 +1,542 @@
|
||||
var chalk = require('chalk');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var archy = require('archy');
|
||||
var Q = require('q');
|
||||
var stringifyObject = require('stringify-object');
|
||||
var os = require('os');
|
||||
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
|
||||
label: 20, // Label max chars
|
||||
sumup: 5 // Amount to sum when the label exceeds
|
||||
};
|
||||
this._colors = {
|
||||
warn: chalk.yellow,
|
||||
error: chalk.red,
|
||||
conflict: chalk.magenta,
|
||||
debug: chalk.gray,
|
||||
default: chalk.cyan
|
||||
};
|
||||
|
||||
this._command = command;
|
||||
this._config = config || {};
|
||||
|
||||
if (this.constructor._wideCommands.indexOf(command) === -1) {
|
||||
this._compact = true;
|
||||
} else {
|
||||
this._compact = process.stdout.columns < 120;
|
||||
}
|
||||
|
||||
var exitOnPipeError = function(err) {
|
||||
if (err.code === 'EPIPE') {
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
// It happens when piping command to "head" util
|
||||
process.stdout.on('error', exitOnPipeError);
|
||||
process.stderr.on('error', exitOnPipeError);
|
||||
}
|
||||
|
||||
StandardRenderer.prototype.end = function(data) {
|
||||
var method = '_' + mout.string.camelCase(this._command);
|
||||
|
||||
if (this[method]) {
|
||||
this[method](data);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.error = function(err) {
|
||||
var str;
|
||||
var stack;
|
||||
|
||||
this._guessOrigin(err);
|
||||
|
||||
err.id = err.code || 'error';
|
||||
err.level = 'error';
|
||||
|
||||
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';
|
||||
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) {
|
||||
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');
|
||||
|
||||
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: ' + 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) {
|
||||
var method = '_' + mout.string.camelCase(log.id) + 'Log';
|
||||
|
||||
this._guessOrigin(log);
|
||||
|
||||
// Call render method for this log entry or the generic one
|
||||
if (this[method]) {
|
||||
this[method](log);
|
||||
} else {
|
||||
this._genericLog(log);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.prompt = function(prompts) {
|
||||
var deferred;
|
||||
|
||||
// Strip colors from the prompt if color is disabled
|
||||
if (!this._config.color) {
|
||||
prompts.forEach(function(prompt) {
|
||||
prompt.message = chalk.stripColor(prompt.message);
|
||||
});
|
||||
}
|
||||
|
||||
// Prompt
|
||||
deferred = Q.defer();
|
||||
var inquirer = require('inquirer');
|
||||
inquirer.prompt(prompts, deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._help = function(data) {
|
||||
var str;
|
||||
var that = this;
|
||||
var specific;
|
||||
|
||||
if (!data.command) {
|
||||
str = template.render('std/help.std', data);
|
||||
that._write(process.stdout, str);
|
||||
} else {
|
||||
// Check if a specific template exists for the command
|
||||
specific = 'std/help-' + data.command.replace(/\s+/g, '/') + '.std';
|
||||
|
||||
if (template.exists(specific)) {
|
||||
str = template.render(specific, data);
|
||||
} else {
|
||||
str = template.render('std/help-generic.std', data);
|
||||
}
|
||||
|
||||
that._write(process.stdout, str);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._install = function(packages) {
|
||||
var str = '';
|
||||
|
||||
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;
|
||||
|
||||
cliTree = this._tree2archy(pkg);
|
||||
str += '\n' + archy(cliTree);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
if (str) {
|
||||
this._write(process.stdout, str);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._update = function(packages) {
|
||||
this._install(packages);
|
||||
};
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
this._write(process.stdout, cliTree);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._search = function(results) {
|
||||
var str = template.render('std/search-results.std', results);
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._info = function(data) {
|
||||
var str = '';
|
||||
var pkgMeta = data;
|
||||
var includeVersions = false;
|
||||
|
||||
// If the response is the whole package info, the package meta
|
||||
// is under the "latest" property
|
||||
if (typeof data === 'object' && data.versions) {
|
||||
pkgMeta = data.latest;
|
||||
includeVersions = true;
|
||||
}
|
||||
|
||||
// Render package meta
|
||||
if (pkgMeta != null) {
|
||||
str += '\n' + this._highlightJson(pkgMeta) + '\n';
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var str = template.render('std/lookup.std', data);
|
||||
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._link = function(data) {
|
||||
this._sizes.id = 4;
|
||||
|
||||
this.log({
|
||||
id: 'link',
|
||||
level: 'info',
|
||||
message: data.dst + ' > ' + data.src
|
||||
});
|
||||
|
||||
// Print also a tree of the installed packages
|
||||
if (data.installed) {
|
||||
this._install(data.installed);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._register = function(data) {
|
||||
var str;
|
||||
|
||||
// If no data passed, it means the user aborted
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
str = '\n' + template.render('std/register.std', data);
|
||||
this._write(process.stdout, str);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._genericLog = function(log) {
|
||||
var stream;
|
||||
var str;
|
||||
|
||||
if (log.level === 'warn') {
|
||||
stream = process.stderr;
|
||||
} else {
|
||||
stream = process.stdout;
|
||||
}
|
||||
|
||||
str = this._prefix(log) + ' ' + log.message + '\n';
|
||||
this._write(stream, 'bower ' + str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._checkoutLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin.split('#')[0] + '#' + log.message;
|
||||
}
|
||||
|
||||
this._genericLog(log);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._progressLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin + ' ' + log.message;
|
||||
}
|
||||
|
||||
this._genericLog(log);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._extractLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin + ' ' + log.message;
|
||||
}
|
||||
|
||||
this._genericLog(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(', ');
|
||||
});
|
||||
|
||||
templatePath = log.data.suitable
|
||||
? 'std/conflict-resolved.std'
|
||||
: 'std/conflict.std';
|
||||
str = template.render(templatePath, log.data);
|
||||
|
||||
this._write(process.stdout, '\n');
|
||||
this._write(process.stdout, str);
|
||||
this._write(process.stdout, '\n');
|
||||
};
|
||||
|
||||
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._cachedEntryLog = function(log) {
|
||||
if (this._compact) {
|
||||
log.message = log.origin;
|
||||
}
|
||||
|
||||
this._genericLog(log);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._guessOrigin = function(log) {
|
||||
var data = log.data;
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.endpoint) {
|
||||
log.origin =
|
||||
data.endpoint.name || (data.registry && data.endpoint.source);
|
||||
|
||||
// Resort to using the resolver name for unnamed endpoints
|
||||
if (!log.origin && data.resolver) {
|
||||
log.origin = data.resolver.name;
|
||||
}
|
||||
|
||||
if (log.origin && data.endpoint.target) {
|
||||
log.origin += '#' + data.endpoint.target;
|
||||
}
|
||||
} else if (data.name) {
|
||||
log.origin = data.name;
|
||||
|
||||
if (data.version) {
|
||||
log.origin += '#' + data.version;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._prefix = function(log) {
|
||||
var label;
|
||||
var length;
|
||||
var nrSpaces;
|
||||
var id = this.constructor._idMappings[log.id] || log.id;
|
||||
var idColor = this._colors[log.level] || this._colors.default;
|
||||
|
||||
if (this._compact) {
|
||||
// If there's not enough space for the id, adjust it
|
||||
// for subsequent logs
|
||||
if (id.length > this._sizes.id) {
|
||||
this._sizes.id = id.length += this._sizes.sumup;
|
||||
}
|
||||
|
||||
return idColor(mout.string.rpad(id, this._sizes.id));
|
||||
}
|
||||
|
||||
// Construct the label
|
||||
label = log.origin || '';
|
||||
length = id.length + label.length + 1;
|
||||
nrSpaces = this._sizes.id + this._sizes.label - length;
|
||||
|
||||
// Ensure at least one space between the label and the id
|
||||
if (nrSpaces < 1) {
|
||||
// Also adjust the label size for subsequent logs
|
||||
this._sizes.label = label.length + this._sizes.sumup;
|
||||
nrSpaces = this._sizes.id + this._sizes.label - length;
|
||||
}
|
||||
|
||||
return chalk.green(label) + mout.string.repeat(' ', nrSpaces) + idColor(id);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._write = function(stream, str) {
|
||||
if (!this._config.color) {
|
||||
str = chalk.stripColor(str);
|
||||
}
|
||||
|
||||
stream.write(str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._highlightJson = function(json) {
|
||||
var cardinal = require('cardinal');
|
||||
|
||||
return cardinal.highlight(stringifyObject(json, { indent: ' ' }), {
|
||||
theme: {
|
||||
String: {
|
||||
_default: function(str) {
|
||||
return chalk.cyan(str);
|
||||
}
|
||||
},
|
||||
Identifier: {
|
||||
_default: function(str) {
|
||||
return chalk.green(str);
|
||||
}
|
||||
}
|
||||
},
|
||||
json: true
|
||||
});
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._tree2archy = function(node) {
|
||||
var dependencies = mout.object.values(node.dependencies);
|
||||
var version = !node.missing
|
||||
? node.pkgMeta._release || node.pkgMeta.version
|
||||
: null;
|
||||
var label = node.endpoint.name + (version ? '#' + version : '');
|
||||
var update;
|
||||
|
||||
if (node.root) {
|
||||
label += ' ' + node.canonicalDir;
|
||||
}
|
||||
|
||||
// State labels
|
||||
if (node.missing) {
|
||||
label += chalk.red(' not installed');
|
||||
return label;
|
||||
}
|
||||
|
||||
if (node.different) {
|
||||
label += chalk.red(' different');
|
||||
}
|
||||
|
||||
if (node.linked) {
|
||||
label += chalk.magenta(' linked');
|
||||
}
|
||||
|
||||
if (node.incompatible) {
|
||||
label +=
|
||||
chalk.yellow(' incompatible') + ' with ' + node.endpoint.target;
|
||||
} else if (node.extraneous) {
|
||||
label += chalk.green(' extraneous');
|
||||
}
|
||||
|
||||
// New versions
|
||||
if (node.update) {
|
||||
update = '';
|
||||
|
||||
if (node.update.target && node.pkgMeta.version !== node.update.target) {
|
||||
update += node.update.target + ' available';
|
||||
}
|
||||
|
||||
if (node.update.latest !== node.update.target) {
|
||||
update += update ? ', ' : '';
|
||||
update += 'latest is ' + node.update.latest;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
label += ' (' + chalk.cyan(update) + ')';
|
||||
}
|
||||
}
|
||||
|
||||
if (!dependencies.length) {
|
||||
return label;
|
||||
}
|
||||
|
||||
return {
|
||||
label: label,
|
||||
nodes: mout.object.values(dependencies).map(this._tree2archy, this)
|
||||
};
|
||||
};
|
||||
|
||||
StandardRenderer._wideCommands = [
|
||||
'install',
|
||||
'update',
|
||||
'link',
|
||||
'info',
|
||||
'home',
|
||||
'register'
|
||||
];
|
||||
StandardRenderer._idMappings = {
|
||||
mutual: 'conflict',
|
||||
'cached-entry': 'cached'
|
||||
};
|
||||
|
||||
module.exports = StandardRenderer;
|
||||
4
lib/renderers/index.js
Normal file
4
lib/renderers/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
Json: require('./JsonRenderer'),
|
||||
Standard: require('./StandardRenderer')
|
||||
};
|
||||
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;
|
||||
22
lib/templates/helpers/condense.js
Normal file
22
lib/templates/helpers/condense.js
Normal file
@@ -0,0 +1,22 @@
|
||||
var mout = require('mout');
|
||||
var leadLinesRegExp = /^\r?\n/;
|
||||
var multipleLinesRegExp = /\r?\n(\r?\n)+/gm;
|
||||
|
||||
function condense(Handlebars) {
|
||||
Handlebars.registerHelper('condense', function(context) {
|
||||
var str = context.fn(this);
|
||||
|
||||
// Remove multiple lines
|
||||
str = str.replace(multipleLinesRegExp, '$1');
|
||||
|
||||
// Remove leading new lines (while keeping indentation)
|
||||
str = str.replace(leadLinesRegExp, '');
|
||||
|
||||
// Remove trailing whitespaces (including new lines);
|
||||
str = mout.string.rtrim(str);
|
||||
|
||||
return str;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = condense;
|
||||
12
lib/templates/helpers/indent.js
Normal file
12
lib/templates/helpers/indent.js
Normal file
@@ -0,0 +1,12 @@
|
||||
var mout = require('mout');
|
||||
|
||||
function indent(Handlebars) {
|
||||
Handlebars.registerHelper('indent', function(context) {
|
||||
var hash = context.hash;
|
||||
var indentStr = mout.string.repeat(' ', parseInt(hash.level, 10));
|
||||
|
||||
return context.fn(this).replace(/\n/g, '\n' + indentStr);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = indent;
|
||||
7
lib/templates/helpers/index.js
Normal file
7
lib/templates/helpers/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
colors: require('./colors'),
|
||||
condense: require('./condense'),
|
||||
indent: require('./indent'),
|
||||
rpad: require('./rpad'),
|
||||
sum: require('./sum')
|
||||
};
|
||||
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;
|
||||
7
lib/templates/helpers/sum.js
Normal file
7
lib/templates/helpers/sum.js
Normal file
@@ -0,0 +1,7 @@
|
||||
function sum(Handlebars) {
|
||||
Handlebars.registerHelper('sum', function(val1, val2) {
|
||||
return val1 + val2;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = sum;
|
||||
11
lib/templates/json/help-cache.json
Normal file
11
lib/templates/json/help-cache.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"command": "cache",
|
||||
"usage": [
|
||||
"cache <command> [<args>] [<options>]"
|
||||
],
|
||||
"commands": {
|
||||
"clean": "Clean cached packages",
|
||||
"list": "List cached packages"
|
||||
},
|
||||
"options": []
|
||||
}
|
||||
16
lib/templates/json/help-cache/clean.json
Normal file
16
lib/templates/json/help-cache/clean.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"command": "cache clean",
|
||||
"description": "Cleans cached packages.",
|
||||
"usage": [
|
||||
"cache clean [<options>]",
|
||||
"cache clean <name> [<name> ...] [<options>]",
|
||||
"cache clean <name>#<version> [<name>#<version> ..] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
lib/templates/json/help-cache/list.json
Normal file
15
lib/templates/json/help-cache/list.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"command": "cache list",
|
||||
"description": "Lists cached packages.",
|
||||
"usage": [
|
||||
"cache list [<options>]",
|
||||
"cache list <name> [<name> ...] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
lib/templates/json/help-home.json
Normal file
16
lib/templates/json/help-home.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"command": "home",
|
||||
"description": "Opens a package homepage into your favorite browser.\n\nIf no <package> is passed, opens the homepage of the local package.",
|
||||
"usage": [
|
||||
"home [<options>]",
|
||||
"home <package> [<options>]",
|
||||
"home <package>#<version> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
lib/templates/json/help-info.json
Normal file
16
lib/templates/json/help-info.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"command": "info",
|
||||
"description": "Displays overall information of a package or of a particular version.",
|
||||
"usage": [
|
||||
"info <package> [<options>]",
|
||||
"info <package> [<property>] [<options>]",
|
||||
"info <package>#<version> [<property>] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
lib/templates/json/help-init.json
Normal file
14
lib/templates/json/help-init.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"command": "init",
|
||||
"description": "Creates a bower.json file based on answers to questions.",
|
||||
"usage": [
|
||||
"init [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
45
lib/templates/json/help-install.json
Normal file
45
lib/templates/json/help-install.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"command": "install",
|
||||
"description": "Installs the project dependencies or a specific set of endpoints.\nEndpoints can have multiple forms:\n- <source>\n- <source>#<target>\n- <name>=<source>#<target>\n\nWhere:\n- <source> is a package URL, physical location or registry name\n- <target> is a valid range, commit, branch, etc.\n- <name> is the name it should have locally.",
|
||||
"usage": [
|
||||
"install [<options>]",
|
||||
"install <endpoint> [<endpoint> ..] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-F",
|
||||
"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",
|
||||
"description": "Show this help message"
|
||||
},
|
||||
{
|
||||
"shorthand": "-p",
|
||||
"flag": "--production",
|
||||
"description": "Do not install project devDependencies"
|
||||
},
|
||||
{
|
||||
"shorthand": "-S",
|
||||
"flag": "--save",
|
||||
"description": "Save installed packages into the project's bower.json dependencies"
|
||||
},
|
||||
{
|
||||
"shorthand": "-D",
|
||||
"flag": "--save-dev",
|
||||
"description": "Save installed packages into the project's bower.json devDependencies"
|
||||
},
|
||||
{
|
||||
"shorthand": "-E",
|
||||
"flag": "--save-exact",
|
||||
"description": "Configure installed packages with an exact version rather than semver"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
lib/templates/json/help-link.json
Normal file
15
lib/templates/json/help-link.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"command": "link",
|
||||
"description": "The link functionality allows developers to easily test their packages.\nLinking is a two-step process.\n\nUsing 'bower link' in a project folder will create a global link.\nThen, in some other package, 'bower link <name>' will create a link in the components folder pointing to the previously created link.\n\nThis allows to easily test a package because changes will be reflected immediately.\nWhen the link is no longer necessary, simply remove it with 'bower uninstall <name>'.",
|
||||
"usage": [
|
||||
"link [<options>]",
|
||||
"link <name> [<local name>] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
24
lib/templates/json/help-list.json
Normal file
24
lib/templates/json/help-list.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"command": "list",
|
||||
"description": "List local packages - and possible updates.",
|
||||
"usage": [
|
||||
"list [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
},
|
||||
{
|
||||
"shorthand": "-p",
|
||||
"flag": "--paths",
|
||||
"description": "Generates a simple JSON source mapping"
|
||||
},
|
||||
{
|
||||
"shorthand": "-r",
|
||||
"flag": "--relative",
|
||||
"description": "Make paths relative to the directory config property, which defaults to bower_components"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
lib/templates/json/help-login.json
Normal file
19
lib/templates/json/help-login.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"command": "login",
|
||||
"description": "Authenticate with GitHub and store credentials to be used later.",
|
||||
"usage": [
|
||||
"login [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
},
|
||||
{
|
||||
"shorthand": "-t",
|
||||
"flag": "--token",
|
||||
"description": "Pass an existing GitHub auth token rather than prompting for username and password."
|
||||
}
|
||||
]
|
||||
}
|
||||
14
lib/templates/json/help-lookup.json
Normal file
14
lib/templates/json/help-lookup.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"command": "lookup",
|
||||
"description": "Look up a single package URL by name.",
|
||||
"usage": [
|
||||
"lookup <name> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
lib/templates/json/help-prune.json
Normal file
14
lib/templates/json/help-prune.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"command": "prune",
|
||||
"description": "Uninstalls local extraneous packages.",
|
||||
"usage": [
|
||||
"prune [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
lib/templates/json/help-register.json
Normal file
19
lib/templates/json/help-register.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"command": "register",
|
||||
"description": "Registers a package.",
|
||||
"usage": [
|
||||
"register <name> <url> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Bypasses confirmation. Bower login is still needed."
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
lib/templates/json/help-search.json
Normal file
14
lib/templates/json/help-search.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"command": "search",
|
||||
"description": "Search for packages by name.",
|
||||
"usage": [
|
||||
"search <name> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
29
lib/templates/json/help-uninstall.json
Normal file
29
lib/templates/json/help-uninstall.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"command": "uninstall",
|
||||
"description": "Uninstalls a package locally from your bower_components directory",
|
||||
"usage": [
|
||||
"uninstall <name> [<name> ..] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Continues uninstallation even after a dependency conflict"
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
},
|
||||
{
|
||||
"shorthand": "-S",
|
||||
"flag": "--save",
|
||||
"description": "Remove uninstalled packages from the project's bower.json dependencies"
|
||||
},
|
||||
{
|
||||
"shorthand": "-D",
|
||||
"flag": "--save-dev",
|
||||
"description": "Remove uninstalled packages from the project's bower.json devDependencies"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
lib/templates/json/help-unregister.json
Normal file
19
lib/templates/json/help-unregister.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"command": "unregister",
|
||||
"description": "Unregisters a package.",
|
||||
"usage": [
|
||||
"unregister <name> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Bypasses confirmation. Bower login is still needed."
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
34
lib/templates/json/help-update.json
Normal file
34
lib/templates/json/help-update.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"command": "update",
|
||||
"description": "Updates installed packages to their newest version according to bower.json.",
|
||||
"usage": [
|
||||
"update <name> [<name> ..] [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-F",
|
||||
"flag": "--force-latest",
|
||||
"description": "Force latest version on conflict"
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
78
lib/templates/json/help.json
Normal file
78
lib/templates/json/help.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"usage": [
|
||||
"<command> [<args>] [<options>]"
|
||||
],
|
||||
"commands": {
|
||||
"cache": "Manage bower cache",
|
||||
"help": "Display help information about Bower",
|
||||
"home": "Opens a package homepage into your favorite browser",
|
||||
"info": "Info of a particular package",
|
||||
"init": "Interactively create a bower.json file",
|
||||
"install": "Install a package locally",
|
||||
"link": "Symlink a package folder",
|
||||
"list": "List local packages - and possible updates",
|
||||
"login": "Authenticate with GitHub and store credentials",
|
||||
"lookup": "Look up a single package URL by name",
|
||||
"prune": "Removes local extraneous packages",
|
||||
"register": "Register a package",
|
||||
"search": "Search for packages by name",
|
||||
"update": "Update a local package",
|
||||
"uninstall": "Remove a local package",
|
||||
"unregister": "Remove a package from the registry",
|
||||
"version": "Bump a package version"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Makes various commands more forceful"
|
||||
},
|
||||
{
|
||||
"shorthand": "-j",
|
||||
"flag": "--json",
|
||||
"description": "Output consumable JSON"
|
||||
},
|
||||
{
|
||||
"shorthand": "-l",
|
||||
"flag": "--loglevel",
|
||||
"description": "What level of logs to report"
|
||||
},
|
||||
{
|
||||
"shorthand": "-o",
|
||||
"flag": "--offline",
|
||||
"description": "Do not hit the network"
|
||||
},
|
||||
{
|
||||
"shorthand": "-q",
|
||||
"flag": "--quiet",
|
||||
"description": "Only output important information"
|
||||
},
|
||||
{
|
||||
"shorthand": "-s",
|
||||
"flag": "--silent",
|
||||
"description": "Do not output anything, besides errors"
|
||||
},
|
||||
{
|
||||
"shorthand": "-V",
|
||||
"flag": "--verbose",
|
||||
"description": "Makes output more verbose"
|
||||
},
|
||||
{
|
||||
"flag": "--allow-root",
|
||||
"description": "Allows running commands as root"
|
||||
},
|
||||
{
|
||||
"shorthand": "-v",
|
||||
"flag": "--version",
|
||||
"description": "Output Bower version"
|
||||
},
|
||||
{
|
||||
"flag": "--no-color",
|
||||
"description": "Disable colors"
|
||||
},
|
||||
{
|
||||
"flag": "--config.interactive=false",
|
||||
"description": "Disable prompts"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
lib/templates/std/conflict-resolved.std
Normal file
9
lib/templates/std/conflict-resolved.std
Normal file
@@ -0,0 +1,9 @@
|
||||
{{#yellow}}Please note that,{{/yellow}}
|
||||
{{#condense}}
|
||||
{{#each picks}}
|
||||
{{#if dependants}}{{#green}}{{dependants}}{{/green}}{{else}}none{{/if}} depends on {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#green}}{{endpoint.name}}#{{pkgMeta._release}}{{/green}}{{/if}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
Resort to using {{#cyan}}{{suitable.endpoint.name}}#{{resolution}}{{/cyan}} which resolved to {{#green}}{{suitable.endpoint.name}}#{{suitable.pkgMeta._release}}{{/green}}
|
||||
Code incompatibilities may occur.
|
||||
11
lib/templates/std/conflict.std
Normal file
11
lib/templates/std/conflict.std
Normal file
@@ -0,0 +1,11 @@
|
||||
{{#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}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
{{#unless saveResolutions}}
|
||||
|
||||
Prefix the choice with ! to persist it to bower.json
|
||||
{{/unless}}
|
||||
17
lib/templates/std/help-cache.std
Normal file
17
lib/templates/std/help-cache.std
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
Usage:
|
||||
|
||||
{{#condense}}
|
||||
{{#each usage}}
|
||||
{{#cyan}}bower{{/cyan}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
Commands:
|
||||
|
||||
{{#condense}}
|
||||
{{#each commands}}
|
||||
{{#rpad minLength="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
{{#rpad minLength="23"}}{{/rpad}}
|
||||
23
lib/templates/std/help-generic.std
Normal file
23
lib/templates/std/help-generic.std
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
Usage:
|
||||
|
||||
{{#condense}}
|
||||
{{#each usage}}
|
||||
{{#cyan}}bower{{/cyan}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
|
||||
Options:
|
||||
|
||||
{{#condense}}
|
||||
{{#each options}}
|
||||
{{#yellow}}{{#rpad minLength="23"}}{{#if shorthand}}{{shorthand}}, {{/if}}{{flag}}{{/rpad}}{{/yellow}} {{description}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
Additionally all global options listed in 'bower help' are available
|
||||
|
||||
Description:
|
||||
|
||||
{{#indent level="4"}}{{description}}{{/indent}}
|
||||
26
lib/templates/std/help.std
Normal file
26
lib/templates/std/help.std
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
Usage:
|
||||
|
||||
{{#condense}}
|
||||
{{#each usage}}
|
||||
{{#cyan}}bower{{/cyan}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
Commands:
|
||||
|
||||
{{#condense}}
|
||||
{{#each commands}}
|
||||
{{#rpad minLength="23"}}{{@key}}{{/rpad}} {{.}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
Options:
|
||||
|
||||
{{#condense}}
|
||||
{{#each options}}
|
||||
{{#yellow}}{{#rpad minLength="23"}}{{#if shorthand}}{{shorthand}}, {{/if}}{{flag}}{{/rpad}}{{/yellow}} {{description}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
See 'bower help <command>' for more information on a specific command.
|
||||
14
lib/templates/std/info.std
Normal file
14
lib/templates/std/info.std
Normal file
@@ -0,0 +1,14 @@
|
||||
{{#if versions}}{{#cyan}}Available versions:{{/cyan}}
|
||||
{{#condense}}
|
||||
{{#versions}}
|
||||
- {{.}}
|
||||
{{/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}}
|
||||
8
lib/templates/std/lookup.std
Normal file
8
lib/templates/std/lookup.std
Normal file
@@ -0,0 +1,8 @@
|
||||
{{#condense}}
|
||||
{{#if url}}
|
||||
{{#cyan}}{{name}}{{/cyan}} {{url}}
|
||||
{{else}}
|
||||
Package not found.
|
||||
{{/if}}
|
||||
{{/condense}}
|
||||
|
||||
5
lib/templates/std/register.std
Normal file
5
lib/templates/std/register.std
Normal file
@@ -0,0 +1,5 @@
|
||||
Package {{#cyan}}{{name}}{{/cyan}} registered successfully!
|
||||
All valid semver tags on {{#cyan}}{{url}}{{/cyan}} will be available as versions.
|
||||
To publish a new version, just release a valid semver tag.
|
||||
|
||||
Run {{#cyan}}bower info {{name}}{{/cyan}} to list the available versions.
|
||||
10
lib/templates/std/search-results.std
Normal file
10
lib/templates/std/search-results.std
Normal file
@@ -0,0 +1,10 @@
|
||||
{{#if .}}Search results:
|
||||
|
||||
{{#condense}}
|
||||
{{#.}}
|
||||
{{#cyan}}{{{name}}}{{/cyan}} {{{url}}}
|
||||
{{/.}}
|
||||
{{/condense}}
|
||||
|
||||
{{else}}No results.
|
||||
{{/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;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user