Compare commits

...

607 Commits

Author SHA1 Message Date
Adam Stankiewicz
febf300fdd Update travis badges and remove old files 2020-04-25 02:22:40 +02:00
Adam Stankiewicz
0eab022654 Last fixes 2020-04-25 01:59:53 +02:00
Adam Stankiewicz
c190c74df9 Remove unncecessary dev dependencies 2020-04-25 01:54:35 +02:00
Adam Stankiewicz
6137633412 Fix windows tests 2020-04-25 01:52:06 +02:00
Adam Stankiewicz
c2cb4f7c48 Add node 14.x 2020-04-25 01:04:15 +02:00
Adam Stankiewicz
225dc0b69f Do not check indent with eslint 2020-04-25 00:40:32 +02:00
Adam Stankiewicz
e0825d1f47 Fix tests 2020-04-25 00:38:06 +02:00
Adam Stankiewicz
b4ecfa5a96 Remove grunt 2020-04-25 00:26:18 +02:00
Adam Stankiewicz
3321382c62 Use existing grunt 2020-04-24 22:51:50 +02:00
Adam Stankiewicz
e4a4cd1ba0 Change order 2020-04-24 20:58:35 +02:00
Adam Stankiewicz
7901011d34 Add github workflows 2020-04-24 20:56:12 +02:00
Adam Stankiewicz
f313129e85 fix 2019-12-10 16:48:54 +01:00
Adam Stankiewicz
fb5a798920 fix 2019-12-10 16:38:50 +01:00
Adam Stankiewicz
6104718892 fix 2019-12-10 16:32:19 +01:00
Adam Stankiewicz
021736f7c5 Update 2019-12-10 16:23:09 +01:00
Adam Stankiewicz
66bd9d1fc4 Update appveyor tests 2019-12-10 16:15:20 +01:00
Adam Stankiewicz
07d40e7fbf Fix 2019-12-10 16:13:01 +01:00
Adam Stankiewicz
16b651139a Fix tests 2019-12-10 16:03:59 +01:00
Adam Stankiewicz
b47ce33e9e Fix 2019-12-10 15:41:29 +01:00
Adam Stankiewicz
4996448d68 Fix tests 2019-12-10 15:32:52 +01:00
Adam Stankiewicz
da1baa055f Fix initialization issue 2019-12-10 15:09:15 +01:00
Adam Stankiewicz
3e294789b5 Fix build 2019-12-10 15:04:54 +01:00
Adam Stankiewicz
45408e9589 Fix 2019-12-10 14:56:36 +01:00
Adam Stankiewicz
cbb9b4415e Try to fix build 2019-12-10 14:52:57 +01:00
Adam Stankiewicz
e3328a4034 Test subpackages on travis and appveyor 2019-12-10 13:01:45 +01:00
dependabot[bot]
4e68c23c87 Bump stringstream from 0.0.5 to 0.0.6 (#2553)
Bumps [stringstream](https://github.com/mhart/StringStream) from 0.0.5 to 0.0.6.
- [Release notes](https://github.com/mhart/StringStream/releases)
- [Commits](https://github.com/mhart/StringStream/compare/v0.0.5...v0.0.6)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-10 12:59:40 +01:00
dependabot[bot]
fcc02c4200 Bump handlebars from 4.0.10 to 4.1.2 (#2552)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.10 to 4.1.2.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.10...v4.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-10 12:33:10 +01:00
dependabot[bot]
d24e7be811 Bump mime from 1.4.0 to 1.6.0 (#2551)
Bumps [mime](https://github.com/broofa/node-mime) from 1.4.0 to 1.6.0.
- [Release notes](https://github.com/broofa/node-mime/releases)
- [Changelog](https://github.com/broofa/node-mime/blob/v1.6.0/CHANGELOG.md)
- [Commits](https://github.com/broofa/node-mime/compare/v1.4.0...v1.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-10 11:41:29 +01:00
dependabot[bot]
746fd3a669 Bump sshpk from 1.13.1 to 1.16.1 (#2550)
Bumps [sshpk](https://github.com/joyent/node-sshpk) from 1.13.1 to 1.16.1.
- [Release notes](https://github.com/joyent/node-sshpk/releases)
- [Commits](https://github.com/joyent/node-sshpk/compare/v1.13.1...v1.16.1)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-10 09:35:48 +01:00
dependabot[bot]
031c7731a4 Bump extend from 3.0.1 to 3.0.2 (#2549)
Bumps [extend](https://github.com/justmoon/node-extend) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/justmoon/node-extend/releases)
- [Changelog](https://github.com/justmoon/node-extend/blob/master/CHANGELOG.md)
- [Commits](https://github.com/justmoon/node-extend/compare/v3.0.1...v3.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-10 09:35:31 +01:00
Adam Stankiewicz
fa62654b9c Test node 10 and 12 on Windows 2019-12-10 09:26:12 +01:00
Adam Stankiewicz
fd2d448b7e Test node 10 and 12 on CI (#2548) 2019-12-10 08:53:36 +01:00
Adam Stankiewicz
997fb2b298 Create FUNDING.yml 2019-11-25 23:18:17 +01:00
Adam Stankiewicz
9c9f332ea0 Add sponsor slots 2019-08-29 17:19:21 +02:00
Adam Stankiewicz
1861fa4355 Add sponsor slots 2019-08-29 17:18:09 +02:00
Adam Stankiewicz
79a539ffb4 Add sponsor slots 2019-06-26 14:30:21 +02:00
Adam Stankiewicz
0608b26364 Add sponsors slots 2019-06-26 14:28:47 +02:00
Adam Stankiewicz
15e5b9709d Add some slots 2019-06-26 14:26:05 +02:00
Adam Stankiewicz
67741b4bfe Bump to 1.8.8 2019-01-23 22:15:30 +01:00
Adam Stankiewicz
45c6bfa86f Fix .tar.gz extract vulnerability 2019-01-23 22:14:48 +01:00
Adam Stankiewicz
4f68fc7daa Update decompress-zip and bump 2019-01-17 23:35:49 +01:00
Adam Stankiewicz
206046b271 Bump to 1.8.6 2019-01-17 14:44:28 +01:00
Adam Stankiewicz
43894f5149 Bump to 1.8.5 2019-01-17 14:19:04 +01:00
Adam Stankiewicz
6390815c5f Update decompress-zip 2019-01-17 13:03:49 +01:00
Adam Stankiewicz
e8b94ecbd0 Mention parcel 2018-06-23 18:06:56 +02:00
Adam Stankiewicz
51feb8f925 Fix release script 2018-04-19 11:07:03 +02:00
Adam Stankiewicz
1c15deadc0 Bump to 1.8.4 2018-03-28 20:43:29 +02:00
Adam Stankiewicz
2aa1f27367 Update publish script 2018-03-28 20:16:27 +02:00
Adam Stankiewicz
275601601a Bump to 1.8.3 2018-03-28 19:52:42 +02:00
Adam Stankiewicz
c22c09546e Disable testing per commit and update changelog 2018-03-28 19:33:16 +02:00
Adam Stankiewicz
6bc778df32 Format with prettier (#2510) 2018-03-28 19:28:43 +02:00
Adam Stankiewicz
451c60ec20 Do not store resolutions if --save is not used, fixes #2344 (#2508) 2018-03-28 18:15:30 +02:00
Adam Stankiewicz
d6a18ae7ee Revert "Update request version in bower-registry-client #2336"
This reverts commit 1e2c27f338.
2018-03-28 18:09:01 +02:00
Adam Stankiewicz
b62faa19a6 Update request version in bower-registry-client #2336 2018-03-28 18:09:01 +02:00
Adam Stankiewicz
50ee729ea2 Allow to disable shorthand resolver (#2507) 2018-03-28 17:58:56 +02:00
Adam Stankiewicz
bb17839bc2 Allow shallow cloning when source is a ssh protocol (#2506) 2018-03-28 17:37:34 +02:00
Michael Lee
5a6ae540f9 Adding support for Arrays in Environment Variable replacement (#2411) 2018-03-28 17:05:13 +02:00
Max Schaefer
1935716660 Fix issues found by lgtm (#2493) 2018-03-28 15:51:53 +02:00
Adam Stankiewicz
add601795f Update year in licenses one last time, fixes #2476 2018-03-28 15:25:36 +02:00
Adam Stankiewicz
8e34328466 Run tests on all node versions and fix test suite, closes #2495 (#2505) 2018-03-28 15:17:31 +02:00
Michael Kühnel
c3e9c94833 Expose non-interactive option to CLI help (#2404) 2018-03-28 13:20:11 +02:00
Madan
dd19bafa37 docs: highlight "-allow-root" (#2496) 2018-03-28 13:17:55 +02:00
Martin Page
74af42c176 Only replace last @ after (if any) last / with # (#2395) 2018-03-28 13:16:09 +02:00
Guido Bouman
a6308bf8f8 Remove duplicate space setting from .editorconfig. (#2480) 2017-09-26 08:11:42 -05:00
Adam Stankiewicz
e1dc0105d2 Reduce node versions on appveyor 2017-09-26 10:57:16 +02:00
Adam Stankiewicz
ce210e4f16 Install grunt on appveyor 2017-09-26 10:55:11 +02:00
Adam Stankiewicz
e483e9bc2c Reduce tested node versions 2017-09-26 10:48:43 +02:00
Adam Stankiewicz
b0c3859699 Fix a test 2017-09-26 10:37:59 +02:00
Adam Stankiewicz
e6d1b2d82e Try to fix appveyor build 2017-09-26 10:36:09 +02:00
Adam Stankiewicz
d4345bb254 Use Yarn on travis and appveyor 2017-09-14 18:08:22 +02:00
Adam Stankiewicz
975f9bdcdb Lock dependencies 2017-09-14 18:05:08 +02:00
Adam Stankiewicz
a969a9c557 Bump bower-config 2017-09-14 18:04:12 +02:00
Adam Stankiewicz
6500b421ce Migrate bower.herokuapp.com to registry.bower.io 2017-09-13 18:59:52 +02:00
Adam Stankiewicz
0641167b96 Remove opencollective for now to prevent installation issues 2017-09-13 18:55:23 +02:00
yanca018
0d03374dab Update LICENSE (#2475)
Update year to 2017
2017-08-14 12:41:32 +02:00
Xavier Damman
765d8e739d Activating Open Collective (#2450) 2017-05-30 18:15:09 +02:00
Thomas Grainger
0bd318de53 Add yarn and webpack recommendation (#2458) 2017-05-19 20:29:15 +02:00
Juan Olvera
aa6b51edc0 Replace gitter references with discord on documentation (#2453) 2017-04-18 18:12:36 -05:00
Adam Stankiewicz
2c2e5309fd Run tests on node 7 as well 2017-03-22 14:03:31 +01:00
Eugene Kenny
b716bc4e3a Prefer exact versions when dissecting dependencies (#2371) 2016-11-08 09:11:29 +01:00
Adam Stankiewicz
bda400634c Bump to 1.8.0 2016-11-07 10:53:30 +01:00
Adam Stankiewicz
b01243ac3c Fix eslint issues 2016-11-07 10:47:29 +01:00
Adam Stankiewicz
89902a6919 Mark release in changelog 2016-11-07 10:46:10 +01:00
Adam Stankiewicz
80308a41a6 Update changelog 2016-11-07 10:34:39 +01:00
Adam Stankiewicz
47cc2262e1 Download tar archives from https://github when possible (#2263) 2016-11-07 02:33:43 +01:00
Guillermo Ignacio Enriquez Gutierrez
f7c5154490 Fix ssl handling by not setting GIT_SSL_NO_VERIFY=false (#2361) 2016-11-07 01:50:57 +01:00
Ali MoezGholami
cba4b2a4cd Allow for removing components with url instead of name (#2368) 2016-11-07 01:48:41 +01:00
Leo.liang
bdabf6a4e6 Show in warning message location of malformed bower.json (#2357) 2016-11-07 01:29:17 +01:00
GvS
7896224384 Improve handling of non-semver versions in git resolver (#2316) 2016-11-07 01:27:14 +01:00
Johannes Faigle
3209cda975 docs: Update package repository information (#2351) 2016-11-07 01:21:27 +01:00
Vytautas Jakutis
38501a0b93 Fix handling of cached releases pluginResolverFactory (#2356) 2016-11-07 01:19:41 +01:00
Adam Stankiewicz
e60d236b25 Add bower-config changes to changelog 2016-11-06 23:04:07 +01:00
Adam Stankiewicz
044896e708 Bump bower-config to 1.4.0 2016-11-06 23:03:56 +01:00
Adam Stankiewicz
fc4c260de4 Update changelog 2016-11-06 22:56:42 +01:00
Adam Stankiewicz
d405917b4a Remove non-working issue stats 2016-07-05 11:16:26 +02:00
Martin Page
22bbb3fcaf Allow @ to be used as a divider with cli: install and info. (#2322) 2016-07-04 23:31:06 +02:00
Adam Stankiewicz
8b6c8eeaa9 Run tests also on 4.0 and 4.1 2016-06-12 14:22:36 +02:00
Adam Stankiewicz
58ddb59104 Revert "Rename .travis.yml to travis.yml"
This reverts commit 9317bb6e33.
2016-06-12 14:19:04 +02:00
Adam Stankiewicz
f26fe38c30 Fix CI badges 2016-06-12 14:18:29 +02:00
Adam Stankiewicz
9317bb6e33 Rename .travis.yml to travis.yml 2016-06-12 14:04:50 +02:00
Adam Stankiewicz
2693bae8ed Improve appveyor.yml 2016-06-12 14:04:28 +02:00
Adam Stankiewicz
8368539a93 Change appveyor badge to new project
[skip ci]
2016-06-12 01:40:45 +02:00
Adam Stankiewicz
8947400487 Disable deploy in appveyor.yml 2016-06-12 01:19:50 +02:00
Adam Stankiewicz
44eeb60529 Improve appveyor.yml 2016-06-12 01:18:32 +02:00
Adam Stankiewicz
2309cd80c6 Add missing return in promise 2016-06-11 02:49:22 +02:00
Adam Stankiewicz
684ab0c0a6 Simplify travis configuration 2016-06-10 20:48:44 +02:00
Adam Stankiewicz
1f6b32a48e Test node 6 also on appveyor 2016-06-10 17:41:11 +02:00
Adam Stankiewicz
e4f0295ef1 Test node 4, 5, and 6 2016-06-10 17:37:53 +02:00
Adam Stankiewicz
85e39cf190 Try to fix svn tests 2016-06-10 17:24:42 +02:00
Adam Stankiewicz
16c134f0f9 Update devDependencies 2016-06-10 17:14:47 +02:00
Adam Stankiewicz
bcbff32716 Pass through eslint 2016-06-10 16:40:46 +02:00
Adam Stankiewicz
809117ea73 Merge bower-endpoint-parser repository 2016-06-10 14:24:55 +02:00
Adam Stankiewicz
af448ba484 Merge bower-registry-client repository 2016-06-10 13:44:43 +02:00
Adam Stankiewicz
89069784bb Merge bower-logger repository 2016-06-10 13:32:03 +02:00
Adam Stankiewicz
57cc6f7a40 Merge bower-json repository 2016-06-10 13:22:15 +02:00
Adam Stankiewicz
cea728c7bc Merge bower-config repository 2016-06-10 13:18:55 +02:00
Adam Stankiewicz
f6be8e5e28 Improve issue template a little 2016-06-09 20:34:14 +02:00
Ben
c6f7ec36de Add issue template (#2305) 2016-06-09 20:29:26 +02:00
Ben
3235b3c0d3 Fix typo in README 2016-06-02 13:36:10 +02:00
Adam Stankiewicz
6ca5d434a4 [docs] Change info at the top of readme 2016-06-02 12:38:49 +02:00
Evan Bowling
eb27ae8fdc Added integration tests for installing nested components (#1513) (#2297) 2016-05-30 11:16:53 +02:00
Ben
353a399f75 Fix test & build for shorthand resolver 2016-05-10 10:46:07 +02:00
Ben
c718541a4e Merge pull request #2282 from evanjbowling/master
Correct typo in command descriptions, followup for #2280
2016-05-08 20:08:40 +02:00
Evan Bowling
1f4372299a Correct typo in command descriptions, followup for #2280 2016-05-08 11:39:45 -05:00
Adam Stankiewicz
e640d0ec5c Improve search / lookup command descriptions, closes #2280 2016-05-06 11:57:26 +02:00
Ben
2e5acfe076 Remove analytics from example config 2016-05-04 19:55:27 +02:00
Ben
19f3c53c70 Merge pull request #54 from evanjbowling/master
Update README.md to be more descriptive
2016-05-04 08:52:38 +02:00
Evan Bowling
26902aec27 Update README.md to be more descriptive 2016-05-03 22:18:05 -05:00
Ben
08feaf2b0a Merge pull request #2271 from rajzshkr/bower-register-info
info message added for bower register
Closing #2075
2016-04-28 12:46:17 +02:00
Ben
db1ed1c08f Fix build errors #2
Related to the change from git:// to https://
2016-04-28 12:32:13 +02:00
Ben
3c2562ca0e Fix failing tests 2016-04-28 12:23:54 +02:00
Raja Sekar
b1f1b8fae3 info message added for bower register 2016-04-28 15:16:10 +05:30
Ben
f1c874b202 Merge pull request #2270 from rajzshkr/bower-cache
fix for 2264
2016-04-28 07:27:15 +02:00
Raja Sekar
4757e7353f fix for 2264 2016-04-28 10:20:19 +05:30
Adam Stankiewicz
249ac47b9c 1.4.0 2016-04-22 00:35:39 +02:00
Adam Stankiewicz
dc59913098 Change default shorthandResolver from git:// to https:// 2016-04-22 00:35:23 +02:00
Oskar Cieślik
343e6ac8bc Implement postuninstall hooks with tests (#2252) 2016-04-14 15:02:07 +02:00
Adam Stankiewicz
e729829174 Make bower version behavior consistent with spec (#2232) 2016-04-12 19:18:36 +02:00
Oskar Cieślik
40e3ee091b Support single-char repo names and package names (#2249) 2016-04-12 19:15:24 +02:00
Oskar Cieślik
8ee2d78779 Add support for registering package with github's orgname/reponame (#2248) 2016-04-12 18:57:44 +02:00
Oskar Cieślik
7c54812ecf Allow to type the entire version when conflict occured (#2243) 2016-04-10 13:24:29 +02:00
Adam Stankiewicz
3251051d20 Fix tests given the new registry 2016-04-10 13:15:57 +02:00
Adam Stankiewicz
81052830f2 Merge pull request #52 from bamsy/topic-unreachable-code
Remove unreachable code from parse function
2016-04-09 18:18:53 +02:00
Justin Barnes
7b2fd5dcd1 Remove unreachable code from parse function 2016-04-09 09:14:04 -05:00
Adam Stankiewicz
bbc9b35cb1 Update grunt to 1.0.1 2016-04-06 13:45:43 +02:00
Adam Stankiewicz
3aebb34f1d Fix typo in changelog 2016-04-05 13:48:21 +02:00
Adam Stankiewicz
8065e5c64a 1.7.9 2016-04-05 13:44:58 +02:00
Adam Stankiewicz
b8e6f36a91 Add changelog entries for 1.7.9 2016-04-05 13:36:52 +02:00
Adam Stankiewicz
12d41aeb8c Warn instead of erroring for invalida package names, #2233 2016-04-05 13:34:06 +02:00
Adam Stankiewicz
8c624bbda6 0.8.1 2016-04-04 21:45:44 +02:00
Adam Stankiewicz
7ee1686cf4 Add changelog for 0.8.1 2016-04-04 21:45:39 +02:00
Adam Stankiewicz
8e181c1792 Make name validation far less strict 2016-04-04 21:37:50 +02:00
Adam Stankiewicz
9569a8074d Add missing changelog entry, #2233 2016-04-04 20:42:55 +02:00
Adam Stankiewicz
e8e4c8fdbc Fix typo in changelog 2016-04-04 19:20:57 +02:00
Adam Stankiewicz
4793fc0d1c Bump year in license 2016-04-04 18:38:28 +02:00
Adam Stankiewicz
4d7cdb0556 0.8.0 2016-04-04 14:11:41 +02:00
Adam Stankiewicz
037bbff17e Update changelog 2016-04-04 14:11:37 +02:00
Adam Stankiewicz
358a73b98e 0.7.1 2016-04-04 13:47:56 +02:00
Adam Stankiewicz
32cbb5a0e8 Update graceful-fs 2016-04-04 13:47:10 +02:00
Adam Stankiewicz
3853d1297e Add changelog entry 2016-04-01 17:28:23 +02:00
Adam Stankiewicz
1e398f999b Update dependencies 2016-04-01 17:27:18 +02:00
Adam Stankiewicz
3a0ea968f4 Update graceful-fs dependency 2016-04-01 17:24:12 +02:00
Adam Stankiewicz
5283a132bc Add name check that reflects reality 2016-02-10 04:33:26 +01:00
Adam Stankiewicz
db1453f7c0 Add findSync and readSync to the README 2016-02-10 02:55:24 +01:00
Adam Stankiewicz
878a228a7d Fix linting error 2016-02-10 02:44:53 +01:00
Adam Stankiewicz
b33041c3ec [test] Remove unnecessary check 2016-02-10 02:42:33 +01:00
Stefan Grönke
c8a6ff38a0 test validation against all registred packages 2016-02-10 02:38:55 +01:00
Adam Stankiewicz
ef67955c21 Bump to 0.7.0 2016-02-10 02:22:34 +01:00
Adam Stankiewicz
36a14b9b37 Add getIssues function and make validate less restrictive 2016-02-10 01:54:31 +01:00
Adam Stankiewicz
c17c725057 Merge pull request #48 from mithun/no-env-in-hooks
Ignore hook scripts for environment variable expansion
2015-12-18 10:00:05 +01:00
Mithun Ayachit
2b31f6c07a Ignore hook scripts for environment variable expansion 2015-12-17 20:38:48 -06:00
Adam Stankiewicz
6a18cde782 Merge pull request #33 from bower/paulohp-patch-1
added coveralls badge.
2015-12-09 13:03:21 +01:00
Adam Stankiewicz
32c5538fc5 Merge pull request #46 from bower/paulohp-patch-1
added coveralls badge
2015-12-09 13:03:03 +01:00
Paulo Pires
57478d86c7 added 2015-12-09 03:53:47 -08:00
Paulo Pires
42107e6fea added coveralls badge 2015-12-09 03:52:47 -08:00
Adam Stankiewicz
9aae3b8d7e Merge pull request #45 from paulohp/grunt-travis
added grunt travis script to .travis.yml
2015-12-08 19:54:52 +01:00
Adam Stankiewicz
34ec39507c Merge pull request #32 from paulohp/sync-functions
added synchronous functions to read and find
2015-12-08 19:20:40 +01:00
Paulo Pires
304bb36bbc Added coveralls support
added missing packages

added test coverage files to gitignore

remove unecessary files

added missing coveralls package
2015-12-08 09:48:42 -08:00
Paulo Pires
376a2de600 added synchronous functions to read and find
added .readSync function

Added coverage
2015-12-08 05:33:38 -08:00
Adam Stankiewicz
9e58bbca31 Merge pull request #31 from paulohp/coveralls
Added coverage report. 📄
2015-12-08 13:54:36 +01:00
Paulo Pires
027a0694f7 Added coverage 2015-12-08 04:16:44 -08:00
Adam Stankiewicz
7bc97a1241 Add changelog entry for array notation in env 2015-12-07 11:45:36 +01:00
Adam Stankiewicz
bd2c253f99 Bump to 1.3.0 2015-12-07 11:43:12 +01:00
Adam Stankiewicz
f1efce7d1f Merge pull request #44 from bower/feature/env
Allow for arrays in env fields (includes a test)
2015-12-07 11:39:17 +01:00
Adam Stankiewicz
844cc5a527 Add test for accept array from process env feature 2015-12-07 11:31:09 +01:00
Peng Wang
0e27b6f813 restore spacing and code formating 2015-12-07 11:31:09 +01:00
Adam Stankiewicz
9219f54718 Merge pull request #43 from prometheansacrifice/adding-changelog
Adding CHANGELOG.md
2015-12-06 11:27:30 +01:00
Manas
5a2272cab1 Adds CHANGELOG.md 2015-12-06 01:19:11 +05:30
Adam Stankiewicz
609607b096 Merge pull request #42 from mithun/allow-env-vars
Allow the use of environment variables in .bowerrc
2015-12-03 12:30:43 +01:00
Mithun Ayachit
e9657668a9 Allow the use of environment variables in .bowerrc
Similar to npmrc

Expand '~/' to user's home directory
2015-12-03 05:20:23 -06:00
Adam Stankiewicz
f18b38cde5 Merge pull request #40 from ryantemple/master
Updated so cwd is set from command line and correct directory tree used
2015-11-28 10:57:22 +01:00
Ryan Temple
e6f1805df0 Added tests 2015-11-27 19:31:24 +00:00
Ryan Temple
4da1b62542 Updated so cwd is set from command line and correct directory tree used 2015-11-27 19:30:08 +00:00
Adam Stankiewicz
6d12ef291b Properly restore env variables if they are undefined 2015-11-27 13:53:36 +01:00
Adam Stankiewicz
c8d5199815 Merge pull request #33 from adriaanthomas/handle-default-ca
Load custom CA file for "default"
2015-11-25 18:32:44 +01:00
Adam Stankiewicz
cd8d397e63 Merge pull request #38 from bower/revert-36-master
Revert "updated to respect the bowerrc in the cwd passed in as command line argument"
2015-11-24 16:53:09 +01:00
Riyadh Al Nur
5a9c099188 Revert "updated to respect the bowerrc in the cwd passed in as command line argument" 2015-11-24 21:25:11 +06:00
Adam Stankiewicz
2137089a70 Merge pull request #37 from riyadhalnur/update-node-versions-travis
Added newer Node versions to Travis
2015-11-24 13:11:17 +01:00
Riyadh Al Nur
fbd02852a3 Added newer Node versions to Travis
ADDED/UPDATED:
- Updated Travis file to not test against `iojs` anymore
- Added versions 4 and 5 of Node to test against on Travis
2015-11-24 13:11:54 +06:00
Riyadh Al Nur
f2584ade24 Merge pull request #36 from ryantemple/master
updated to respect the bowerrc in the cwd passed in as command line argument
2015-11-24 13:06:05 +06:00
Ryan Temple
50bfd14968 updated to respect the bowerrc in the cwd passed in as command line argument 2015-11-23 22:45:45 +00:00
Adam Stankiewicz
d867095f50 Merge pull request #34 from kevcenteno/bowerrc-dir-error
Show error if .bowerrc is a directory
2015-11-19 18:17:24 +01:00
kevcenteno
e51bf20e72 Show error if .bowerrc is a directory 2015-11-19 10:35:22 -05:00
Adriaan Thomas
15f8a30cd4 Also load custom CA file for "default" 2015-11-17 13:31:40 +01:00
Adam Stankiewicz
b7c19695e7 Bump to 1.2.2 2015-10-16 11:38:06 +02:00
Adam Stankiewicz
b85cf2683c Fix registry configuration expanding, closes bower/bower#1950 2015-10-16 11:37:41 +02:00
Adam Stankiewicz
8df5970300 Bump to 1.2.1 2015-10-14 21:30:26 +02:00
Adam Stankiewicz
db265d471f fix: Setting HTTP_PROXY on Windows (case insensitivity) 2015-10-14 21:30:09 +02:00
Adam Stankiewicz
0441e16bdb Bump to 1.2.0 2015-09-28 19:37:58 +02:00
Adam Stankiewicz
30a489535e Prevent defaulting cwd to process.cwd() 2015-09-28 19:37:44 +02:00
Adam Stankiewicz
ac88ece259 Bump to 1.0.0 2015-09-28 19:15:26 +02:00
Adam Stankiewicz
9b45c76744 Change ~ ranges to ^ 2015-09-28 19:14:58 +02:00
Adam Stankiewicz
059a5f83b7 Require using explicitly bower-config 2015-09-28 19:14:58 +02:00
Adam Stankiewicz
cf5cd61995 Pass proxy configuration via env variables instead of directly, closes #22 2015-09-28 19:14:58 +02:00
Adam Stankiewicz
a499cc5103 Update bower.json dependency 2015-09-28 19:14:58 +02:00
Adam Stankiewicz
bebb4fb33b Fix one more upper case spelling 2015-09-27 19:46:43 +02:00
Adam Stankiewicz
2c243ea5b7 tests: Fix camel casing 2015-09-27 19:42:16 +02:00
Adam Stankiewicz
f53100d8d3 Bump version to 1.1.2 2015-09-27 17:08:55 +02:00
Adam Stankiewicz
acbe60cdf1 Perform only camelcase normalization before merging configs 2015-09-27 17:08:42 +02:00
Adam Stankiewicz
4c7f37e0f8 Bump to 1.1.1 2015-09-27 16:41:44 +02:00
Adam Stankiewicz
3ba696937c Merge extra config after normalisation 2015-09-27 16:41:32 +02:00
Adam Stankiewicz
1647994471 Bump to 1.1.0 2015-09-27 16:35:01 +02:00
Adam Stankiewicz
674e09dc56 Allow for overwriting options 2015-09-27 16:34:51 +02:00
Adam Stankiewicz
4c129d470f Bump to 1.0.1 2015-09-27 15:03:33 +02:00
Adam Stankiewicz
c630f01baa Update dependencies 2015-09-27 15:02:48 +02:00
Adam Stankiewicz
6018fc13b2 Bump version to 1.0.0 2015-09-27 14:45:53 +02:00
Adam Stankiewicz
c4fc6cd0e2 Typo in readme 2015-09-27 14:41:54 +02:00
Adam Stankiewicz
eed8735238 Fix formatting in the readme 2015-09-27 14:41:17 +02:00
Adam Stankiewicz
8c0155e8bd Normalize config right away in load method 2015-09-27 14:33:22 +02:00
Adam Stankiewicz
b2d4412e59 Remove unused methods and better document .restore method 2015-09-27 14:14:35 +02:00
John Andersen
4e3e45a88b Support for setting and restoring proxy related env variables 2015-09-27 14:05:17 +02:00
Adam Stankiewicz
af9b386d8a Suport backward-compatible ca certificate embedding 2015-09-27 13:34:17 +02:00
Adriaan Thomas
201b8a3bc6 Load CA file contents instead of keeping the path, so the request library can use it. 2015-09-27 13:34:17 +02:00
David DeSandro
a23f66d889 Merge pull request #27 from cvrebert/patch-1
0.6.0
2015-07-22 15:31:40 -04:00
Chris Rebert
a1596bb63c 0.6.0 2015-07-13 19:17:20 -07:00
David DeSandro
19af145166 Merge pull request #25 from cvrebert/normalize
Remove non-spec-compliant comma-separated string interpretation of main
2015-07-01 14:14:15 -04:00
David DeSandro
4ed81be0c6 Merge pull request #26 from cvrebert/validate-main
Validate that `main` conforms to the bower.json spec
2015-07-01 08:55:23 -04:00
Chris Rebert
1c62adcdb6 Validate that main conforms to the bower.json spec 2015-06-30 23:16:00 -07:00
Chris Rebert
95f46930a5 Remove non-spec-compliant comma-separated string interpretation of main
See https://github.com/bower/bower.json-spec/#main
2015-06-30 23:04:13 -07:00
Adam Stankiewicz
76dd504589 Merge pull request #27 from pdehaan/patch-1
Update license attribute
2015-05-21 12:31:51 +02:00
Peter deHaan
938c69c816 Update license attribute
specifying the type and URL is deprecated:

https://docs.npmjs.com/files/package.json#license
http://npm1k.org/
2015-05-20 16:57:57 -07:00
Sindre Sorhus
d5fc402a89 0.5.0 2015-05-19 22:07:12 +02:00
Sindre Sorhus
065a7a1f1b bump deps 2015-05-19 22:06:59 +02:00
Sindre Sorhus
ccadffea73 Update package.json 2015-04-28 12:54:04 +07:00
Sindre Sorhus
b3390ce201 use package.json files object to reduce the package size
https://docs.npmjs.com/files/package.json#files
2015-04-28 12:50:37 +07:00
Sindre Sorhus
6039f6c691 minor package.json tweak 2015-04-28 12:50:10 +07:00
Sindre Sorhus
c65cdc8699 use package.json files object to reduce the package size
https://docs.npmjs.com/files/package.json#files
2015-04-28 12:42:26 +07:00
Sindre Sorhus
7603886e04 minor package.json tweak 2015-04-28 12:41:43 +07:00
Sindre Sorhus
406a96e4d1 use package.json files object to reduce the package size
https://docs.npmjs.com/files/package.json#files
2015-04-28 12:40:35 +07:00
Adam Stankiewicz
7d74d7d8f6 0.3.0 2015-04-01 00:24:38 -07:00
Adam Stankiewicz
e98d8139bc Bump bower-config version (bower/config#25) 2015-04-01 00:24:22 -07:00
Adam Stankiewicz
8a47aab01d Bump to 0.6.1 2015-03-31 22:27:07 -07:00
Adam Stankiewicz
395b208a0c Merge all .bowerrc upwards directory tree, fixes #25 2015-03-31 22:26:39 -07:00
Adam Stankiewicz
f6178c2f75 Bump to 0.6.0 2015-03-30 01:19:35 -07:00
Adam Stankiewicz
f1e04e5629 Merge branch 'kodypeterson-fixes-bower/bower/1689'
Conflicts:
	test/test.js
2015-03-30 01:15:56 -07:00
Adam Stankiewicz
cc913a728a Merge pull request #23 from zanona/npm-config
Allow NPM config variables. Resolve bower/bower#1711
2015-03-30 01:14:02 -07:00
Kody Peterson
f8c179b153 Cleanup 2015-03-27 11:05:41 -04:00
Kody Peterson
bea46fb879 Children should be source 2015-03-26 14:32:35 -04:00
Kody Peterson
379de05a61 Correct the logic 2015-03-26 14:25:42 -04:00
Kody Peterson
c2f222760a Fixes https://github.com/bower/bower/issues/1689 and actually adds tests and testing ability 2015-03-25 13:42:44 -04:00
Marcus Zanona
9aef3b7f1d Allow NPM config variables. Resolve bower/bower#1711 2015-03-12 17:42:32 +00:00
Adam Stankiewicz
c93bbbd302 Bump to 0.2.4 2015-03-04 22:38:12 -08:00
Adam Stankiewicz
009e5ce0c8 Better error messages for unregistering 2015-03-04 22:25:46 -08:00
Adam Stankiewicz
61bb4f53c0 Increase timeout for test 2015-03-04 22:25:46 -08:00
Sindre Sorhus
b6dd5e445e minor tweaks 2015-02-08 21:12:31 +07:00
Sindre Sorhus
6f4b77a440 Update .travis.yml 2015-02-08 20:47:09 +07:00
Sindre Sorhus
463584ea10 Update .travis.yml 2015-02-08 20:46:55 +07:00
Sindre Sorhus
fd8d603831 Update .travis.yml 2015-02-08 20:45:29 +07:00
Sindre Sorhus
a1ec83b002 0.2.3 2015-01-22 17:43:09 +08:00
Sindre Sorhus
041d3f2843 minor tweaks 2015-01-22 17:23:36 +08:00
Sindre Sorhus
0b22127906 Merge pull request #19 from tchajed/duplicate-package-error
Change duplicate package status code to 403
2015-01-22 16:46:52 +08:00
Sindre Sorhus
0b0b507827 Merge pull request #20 from masakura/fix-node11-proxy-failed
Fix error bower command behind proxy.
2015-01-22 16:45:12 +08:00
Tomo Masakura
16de289942 Fix error bower command behind proxy. 2015-01-21 13:57:28 +09:00
patrick kettner
02dc97e413 Merge pull request #18 from tmcw/error-message
Adds an error message to errors. Fixes #16
2015-01-13 10:43:04 -08:00
Adam Stankiewicz
703eae72eb Bump to 0.2.2 2015-01-05 01:50:06 +01:00
Adam Stankiewicz
6a629e963c fix: Duplicate package has now 403 error code
According to change in bower/registry#74ae4d34b68df239b8db876a4f871b100ef3c540
2015-01-05 01:47:00 +01:00
Tej Chajed
f242e60c1a Change duplicate package status code to 403
Registry no longer sends 406 so clients would previously just see
"Unknown error: 403 if they tried to register a duplicate package.
2014-11-01 09:08:58 -04:00
Ray Shan
6d335abe21 Merge pull request #21 from bower/fix/appdata
Use local appdata for cache, fixes bower/bower#1536
2014-09-23 09:58:17 -07:00
Adam Stankiewicz
179b8a28b4 Use local appdata for cache, fixes bower/bower#1536 2014-09-23 16:43:22 +02:00
Tom MacWright
fc13328e2a Adds an error message to errors. Fixes #16
This could be better if it could distinguish between server errors
and actually unknown errors (known unknowns), but that would require
the server to provide JSON errors.
2014-08-19 12:30:04 -04:00
Ray Shan
b3f28fac64 Update README.md 2014-08-12 11:08:42 -07:00
Ray Shan
f65af7a308 Update README.md 2014-08-12 11:06:53 -07:00
Ray Shan
559f50a3e3 Merge pull request #3 from ncthis/patch-1
Fixing minor syntax errors
2014-08-12 11:05:27 -07:00
Sindre Sorhus
18b809314c Update README.md 2014-08-12 11:06:11 +02:00
Sindre Sorhus
aa1f819c0d Merge pull request #20 from rayshan/patch-1
Update README.md w/ npm instructions
2014-08-12 11:04:43 +02:00
Ray Shan
5a8cecf499 Update README.md 2014-08-11 17:56:54 -07:00
Adam Stankiewicz
79f362abee Merge pull request #21 from bower/docs_npm
Update README.md
2014-08-12 00:08:17 +02:00
Ray Shan
42db74b522 Update README.md 2014-08-11 15:03:34 -07:00
Adam Stankiewicz
3309e9f53f Merge pull request #19 from stephanebachelier/patch-1
fix typo in README
2014-08-08 17:02:10 +02:00
Stéphane Bachelier
ae3a017143 fix typo in README 2014-08-08 16:59:47 +02:00
Adam Stankiewicz
43d7a11ba8 Bump version to 0.2.2 2014-06-20 23:01:20 +02:00
Adam Stankiewicz
02e12e17d6 Prepend # to ambiguous branch name, fixes bower/bower#1308 2014-06-20 23:00:14 +02:00
Adam Stankiewicz
4f838685d6 Bump to 0.5.2 2014-06-09 03:28:08 +02:00
Adam Stankiewicz
6339ba09f0 "Merge pull request #18 from mdasberg/master\n\nFix for Overriding .bowerrc with a relative tmp path that breaks downloading of bower modules with ignores" 2014-06-09 03:26:33 +02:00
Sindre Sorhus
23c2e82c97 Merge pull request #18 from shinnn/master
Update dependencies and .jshintrc
2014-06-03 11:22:57 +02:00
Shinnosuke Watanabe
7138d3518e Fix CI setting to pass the test against Node v0.8 2014-06-02 17:22:49 +09:00
Shinnosuke Watanabe
f852325906 Update graceful-fs and mocha 2014-06-02 14:43:08 +09:00
Shinnosuke Watanabe
3c9082ece3 Update .jshintrc for JSHint v2.5.x 2014-06-02 14:42:43 +09:00
Adam Stankiewicz
98c77ffe18 Bump to 0.2.1 2014-05-21 14:20:25 +02:00
Mischa Dasberg
b95d5f9f23 moved tmp resolve from load to normalize method 2014-05-21 11:02:19 +02:00
Nadeesha Cabral
adf59d78d7 Fixing minor syntax errors 2014-05-21 12:22:13 +05:30
Adam Stankiewicz
904ae3eab7 Merge pull request #9 from geigerzaehler/update-config-version
Use the same version of bower-config as bower
2014-05-21 02:17:33 +02:00
Adam Stankiewicz
44d2309700 Bump version 2014-05-21 00:17:40 +02:00
Adam Stankiewicz
5dd6a3883a Add info about unimplemented features, closes #11 2014-05-20 23:47:47 +02:00
Mischa Dasberg
5f0a3fe1c1 See: https://github.com/bower/bower/issues/1299 and https://github.com/bower/config/issues/17 2014-05-13 14:25:45 +02:00
Sindre Sorhus
bc3079332c Merge pull request #7 from sheerun/patch-1
perf: Use the same mout version as bower
2014-04-12 23:22:47 +02:00
Sindre Sorhus
3c0395b19f Merge pull request #16 from sheerun/fix/mout
perf: Use only relevant parts of mout
2014-04-12 23:21:41 +02:00
Sindre Sorhus
9e7b591c78 Merge pull request #8 from sheerun/fix/mout
Use only relevant parts of mout
2014-04-12 23:21:18 +02:00
Adam Stankiewicz
b6107a1198 Use only relevant parts of mout 2014-04-12 22:23:12 +02:00
Adam Stankiewicz
2d94018f12 perf: Use only relevant parts of mout 2014-04-12 22:17:56 +02:00
Adam Stankiewicz
42f0268829 perf: Use the same mout version as bower 2014-04-12 22:10:37 +02:00
shinnn
4812c380c4 Add a validation for description length
closes #17
2014-04-05 13:06:09 +02:00
Sven Lito
0e833c155a Merge pull request #10 from wibblymat/unregister
Add 'unregister' feature
2014-04-01 17:32:33 +02:00
Mat Scales
9ebb1b17d1 Added tests and fixed code-style 2014-04-01 15:57:08 +01:00
Mat Scales
3397cf053c Removed a debug line 2014-04-01 15:29:35 +01:00
Mat Scales
6e73b5934e Add 'unregister' feature 2014-04-01 13:26:35 +01:00
Thomas Scholtes
a5c49d89d5 Use tilde to match bower-config version 2014-02-27 00:26:34 +01:00
Thomas Scholtes
1f9a92e6ad Use the same version of bower-config as bower
Make loading this package from bower faster. Used `>=` to specify version so it
doesn't break that fast when bower starts using a version `>=0.6.0`.
2014-02-26 15:38:10 +01:00
Sindre Sorhus
6280611aea Merge pull request #13 from geigerzaehler/master
Use same mout version as bower
2014-02-25 21:51:08 +01:00
Thomas Scholtes
20c0e6e12b Use same mout version as bower
We don't need to load mout twice, which takes a lot of time.
2014-02-25 19:53:50 +01:00
reiz
d3ccc73796 Adding validation for package name
Closes #15
2014-01-31 14:54:40 +01:00
Sven Lito
53d3ac570e Merge pull request #7 from clientlab/master
fix a bug
2013-12-26 07:32:13 -08:00
周培公
237022baae add a test.
add a test 'calling the lookup instance method with two registries, and
the first missing', and adjust the test sequence.
2013-12-26 08:08:29 +08:00
周培公
bccfd7bbbb fix a bug. 2013-12-24 15:03:03 +08:00
Sven Lito
597853cd6c bump version 2013-12-11 10:43:50 +00:00
Sven Lito
1bd6568f94 Merge pull request #6 from xiaojue/master
add headers for request to fix userAgent not work.
2013-12-11 02:39:42 -08:00
xiaojue
4b2235aef2 add userAgent test 2013-12-11 18:32:13 +08:00
xiaojue
faf1c26669 add headers for request to fix userAgent not work. 2013-12-11 15:17:08 +08:00
Mat Scales
c385c08e2f Close GH-2: Add checkbox prompt. 2013-12-06 22:11:00 +00:00
André Cruz
ab7e7ac12a Bump version. 2013-09-28 15:11:01 +01:00
Sven Lito
3788e8d7b3 Merge pull request #5 from bower/md5
Append a truncated hash to prevent case issues in case insensitive fs.
2013-09-28 02:15:53 -07:00
André Cruz
041290e1c7 Append a truncated hash to prevent case issues in case insensitive fs. 2013-09-28 00:54:33 +01:00
André Cruz
1099e786df Bump version. 2013-08-29 19:51:13 +01:00
André Cruz
be95169c1b Add DEFAULT_REGISTRY, #6. 2013-08-29 19:50:46 +01:00
André Cruz
a899cb48b3 Bump version. 2013-08-28 08:21:13 +01:00
André Cruz
ed8ac01f07 Do not crash if home is not set. 2013-08-28 08:20:55 +01:00
André Cruz
12cde3ddce Doc typo. 2013-08-21 18:48:21 +01:00
André Cruz
860d70551f Bump version. 2013-08-21 18:46:05 +01:00
André Cruz
6db6fcc414 Small tweaks to last PR. 2013-08-21 18:45:46 +01:00
André Cruz
25c229de73 Merge pull request #9 from alexwhitman/nested-env-vars
Allow nested environment variables using '__'
2013-08-21 10:26:31 -07:00
Alex Whitman
d954a54017 Handle nesting in environment variables
Nested configuration variables can now be specified by environment
variable. Levels are split by '__' (double underscore). Single
underscores are converted to '-'.

`bower_foo__bar-baz` -> `foo.bar-baz`
2013-08-21 11:41:46 +01:00
André Cruz
78bbf1f04f Bump version. 2013-08-20 21:48:55 +01:00
André Cruz
52938202bd Fix target not being added if source does not look like a source. 2013-08-20 21:48:26 +01:00
André Cruz
0f29818030 Bump version. 2013-08-20 01:03:01 +01:00
André Cruz
dd39a25dd0 Update deps. 2013-08-20 01:02:39 +01:00
André Cruz
6ed4be9135 Merge branch 'master' of github.com:bower/logger 2013-08-20 00:42:32 +01:00
André Cruz
02b6a21276 Bump version. 2013-08-20 00:42:17 +01:00
André Cruz
794ca573b8 Trim answers automatically. 2013-08-20 00:41:15 +01:00
André Cruz
032f771996 Simplify. 2013-08-19 09:01:45 +01:00
Andre Cruz
3dfd7a9ab1 Bump version. 2013-08-19 00:57:39 +01:00
Andre Cruz
1713e5e2eb Minor improvement in argv.config parsing. 2013-08-19 00:57:30 +01:00
Andre Cruz
1d7342573b Bump version. 2013-08-19 00:43:41 +01:00
Andre Cruz
64fc295ecc Add prompt(). 2013-08-19 00:43:17 +01:00
Andre Cruz
6b6dc8311a Bump version. 2013-08-18 17:29:00 +01:00
Andre Cruz
b1d8c3c1e3 Interactive is now set to auto (null). 2013-08-18 17:28:35 +01:00
Andre Cruz
71037cb482 Bump version. 2013-08-18 13:08:43 +01:00
Andre Cruz
1f4e5cadd2 Generate a fake user instead of using 'unknown'. 2013-08-18 13:08:15 +01:00
André Cruz
0de9cc82f6 Bump version. 2013-08-16 09:43:39 +01:00
André Cruz
7dba46df9b Temp folder is now suffixed with the user and "bower". 2013-08-16 09:30:40 +01:00
André Cruz
9cb09feb65 Bump version. 2013-08-14 09:10:24 +01:00
André Cruz
fb084fa4cd Cast buffer to string. 2013-08-14 09:10:08 +01:00
André Cruz
846b8fb57e Bump version 2013-08-14 08:36:54 +01:00
Salehen Shovon Rahman
cb649830a0 Close GH-7: Empty .bowerrc files should not throw an error.. 2013-08-14 08:35:00 +01:00
André Cruz
89510f40d3 Bump version. 2013-08-11 18:24:25 +01:00
André Cruz
9f2207eb1f Change git folder to empty (was not being used anyway). 2013-08-11 18:24:13 +01:00
André Cruz
dfb18b305d Bump version. 2013-08-11 14:46:33 +01:00
André Cruz
1606395546 Close GH-12: Ignore component(1) files.. 2013-08-11 14:45:55 +01:00
André Cruz
8dbd79d49b Bump version. 2013-08-07 08:29:40 +01:00
André Cruz
e2c67fa25a Use a known user agent by default when a proxy. 2013-08-07 08:28:50 +01:00
André Cruz
cf85177c7f DRY. 2013-08-06 09:12:13 +01:00
André Cruz
bc4a0f448b Bump version. 2013-08-06 00:02:54 +01:00
André Cruz
14ef86456f Typo. 2013-08-06 00:02:31 +01:00
André Cruz
12efc85baf Bump version. 2013-08-05 23:40:52 +01:00
André Cruz
68124dfdbe Bum version. 2013-08-05 23:39:15 +01:00
André Cruz
ee4158e90b Append the username when using the temporary folder. 2013-08-05 23:38:19 +01:00
André Cruz
0b3b7efccf Minor improvement. 2013-08-05 23:36:11 +01:00
André Cruz
6064269936 Bump version. 2013-08-05 14:22:51 +01:00
André Cruz
d0929896cf Update README. 2013-08-05 14:22:41 +01:00
André Cruz
990e87de1f Tweaks to the last PR. 2013-08-05 14:16:53 +01:00
Nick Heiner
8da47dcd00 Close GH-10: Find .bower.json if it exists. 2013-08-05 14:10:57 +01:00
André Cruz
c63d88d987 Bump version. 2013-08-03 12:44:18 +01:00
André Cruz
c21bde192b Fix failing test. 2013-08-03 12:44:07 +01:00
André Cruz
39f1f8aff5 Improve retry error messages.
Also fixed "undefined" being printed as the url.
2013-08-03 12:38:21 +01:00
André Cruz
8b699c58ae Bump rc version. 2013-08-02 21:12:54 +01:00
André Cruz
743a97c784 Merge branch 'master' of github.com:bower/config 2013-08-02 21:10:39 +01:00
André Cruz
ea7ae5698a Do not set undefined values in array. 2013-08-02 21:10:31 +01:00
André Cruz
3ed9b3ec39 Fix default values being mutated. 2013-08-02 21:10:10 +01:00
André Cruz
e8f3e3b88a Set timeout default value to 30sec. 2013-08-02 21:09:54 +01:00
André Cruz
82d16fbfde Change timeout to 30sec. 2013-08-02 08:25:35 +01:00
André Cruz
766dcd0dd5 Bump version. 2013-07-29 23:14:27 +01:00
André Cruz
87302d6d86 Merge pull request #3 from bower/trim
Add trim to everything.
2013-07-29 15:11:07 -07:00
André Cruz
05b94d1d15 Add more tests and fix stuff. 2013-07-28 18:54:04 +01:00
André Cruz
ed0c93aec3 Add trim to everything. 2013-07-28 18:21:24 +01:00
André Cruz
03b4467173 Update deps. 2013-07-28 03:20:46 +01:00
André Cruz
f027cc6a3e Bump version. 2013-07-28 03:03:56 +01:00
André Cruz
634ce6829a Use bower-config to normalise the config object. 2013-07-28 03:03:39 +01:00
André Cruz
64b5f9af78 Bump to 0.2.0 rc1. 2013-07-28 02:58:08 +01:00
André Cruz
9269fcb8a7 Add normalisation, fixes #5.
Refactor a lot of code.
2013-07-28 02:57:24 +01:00
André Cruz
f0d29cb755 Bump to 0.1.0. 2013-07-27 22:30:51 +01:00
André Cruz
897e0f1ba6 Bump to 0.1.0. 2013-07-27 22:28:56 +01:00
André Cruz
4cfa94d304 Bump to 0.2.0. 2013-07-27 22:26:17 +01:00
André Cruz
5e2abb8a33 Add .file property to errors throw in read(). 2013-07-27 22:21:21 +01:00
Sven Lito
0dc5052e36 Close GH-9: adds grunt. 2013-07-27 20:44:16 +01:00
Sven Lito
774ad1e2ff remove inline jshint rule
this setting is covered by local node:true setting
2013-07-27 20:43:33 +01:00
Sven Lito
52e91cc309 Merge pull request #8 from bower/better-api
Changes to the api.
2013-07-27 12:09:12 -07:00
André Cruz
d3e6274939 Remove unused var. 2013-07-27 11:25:38 +01:00
Sven Lito
ec904eb8a4 adds list tests [#4] 2013-07-26 16:02:42 +01:00
André Cruz
95a09c5463 Fix README typos as suggested. 2013-07-25 23:31:33 +01:00
André Cruz
125fb598d5 Adjust README to the api changes. 2013-07-25 22:19:09 +01:00
André Cruz
f8c13f939c Changes to the api.
- Read now accepts directories and finds the bower json to read inside
- Add normalise and validate methods
- Add options to the parse and read methods to enable/disable normalisation and validation.
2013-07-25 22:06:42 +01:00
André Cruz
c846b24ebb Bump rc. 2013-07-24 09:00:53 +01:00
Sven Lito
85324d9109 Merge pull request #3 from bower/lookup-bug
Fix lookup and list bug for multiple registries.
2013-07-24 00:10:36 -07:00
André Cruz
10b410d46f Fix multiple registries in the list method. 2013-07-23 23:20:56 +01:00
André Cruz
c87fe7c265 Fix lookup bug for multiple registries.
Improve and fix tests.
2013-07-23 22:56:17 +01:00
André Cruz
ce0984573b Fix tests. 2013-07-23 22:55:00 +01:00
André Cruz
b38c3a5035 Add missing dep. 2013-07-23 22:51:24 +01:00
André Cruz
15dca65bd1 Set timeout to 60sec. 2013-07-23 22:50:40 +01:00
André Cruz
e1aa43147d Minor tweaks. 2013-07-23 22:49:58 +01:00
André Cruz
057b18e4be Bump to 0.1.0 2013-07-23 21:01:51 +01:00
André Cruz
d4f2ced6a3 Add paths tests, simply with regexp. 2013-07-23 21:01:30 +01:00
André Cruz
0b6c92fcd2 Merge pull request #2 from redking/master
Endpoints with backslashes should be recognized as sources
2013-07-23 12:57:27 -07:00
Sven Lito
4491e71ee2 Merge pull request #2 from neoziro/fix-registry-search
Fix search in multiple registries
2013-07-23 09:07:23 -07:00
Greg Bergé
d09f78801c fix search in multiple registries 2013-07-23 18:00:21 +02:00
Cormac Flynn
f26ea32897 Endpoints with backslashes should be recognized as sources 2013-07-23 15:41:22 +02:00
André Cruz
86dbea7ddb Update reps. 2013-07-20 23:18:09 +01:00
André Cruz
4221ddbb35 Bump rc. 2013-07-20 22:06:34 +01:00
André Cruz
eeb0c22d90 Integrate logger. 2013-07-20 22:05:36 +01:00
André Cruz
eb801e66ae Remove replay from register. 2013-07-20 22:05:07 +01:00
André Cruz
4d0d4ca6ea README typos. 2013-07-20 20:22:30 +01:00
André Cruz
56cdae67c3 Document log levels 2013-07-20 20:21:00 +01:00
André Cruz
b50017cad4 Initial commit. 2013-07-20 20:18:39 +01:00
André Cruz
c85c38cde3 Bump rc version. 2013-07-20 19:49:41 +01:00
André Cruz
f606eda18d Add replay to requests. 2013-07-20 19:49:13 +01:00
André Cruz
08c9e2dde3 CS. 2013-07-20 19:48:42 +01:00
André Cruz
882bf7b020 Bump rc version. 2013-07-18 19:31:52 +01:00
André Cruz
da4e70bc60 Merge branch 'master' of github.com:bower/endpoint-parser 2013-07-18 19:31:02 +01:00
André Cruz
f75f720c8a Merge branch 'master' of github.com:bower/registry-client 2013-07-18 19:30:44 +01:00
André Cruz
8d3aff5ff1 Update editorconfig. 2013-07-18 19:30:27 +01:00
André Cruz
4e8c9078f7 Update editorconfig. 2013-07-18 19:30:13 +01:00
André Cruz
41e4efcf1f Merge branch 'master' of github.com:bower/config 2013-07-18 19:29:38 +01:00
André Cruz
f55e6138a5 Remove git config. 2013-07-18 19:29:30 +01:00
André Cruz
147e24d835 Update editorconfig. 2013-07-18 19:29:22 +01:00
André Cruz
4a94858ed1 Update editorconfig. 2013-07-18 19:29:11 +01:00
André Cruz
12c90bae04 Another README typo. 2013-07-18 08:13:10 +01:00
André Cruz
6f7f10b2f7 Update README.md 2013-07-16 20:03:16 +01:00
André Cruz
430a2ea2f6 Bump rc. 2013-07-16 19:52:46 +01:00
André Cruz
87569617ae Add spec link. 2013-07-16 19:52:32 +01:00
André Cruz
b17beaccf1 Add timeout config. 2013-07-16 19:52:24 +01:00
Sven Lito
cddba64151 call next instead of done 2013-07-16 16:10:07 +01:00
André Cruz
a006bfeb24 Fix git@ endpoints not being interpreted as sources. 2013-07-16 07:09:20 +01:00
André Cruz
71e1a8666d Add tests and made some changes. 2013-07-15 22:29:23 +01:00
André Cruz
16e7872a82 Typo. 2013-07-15 00:42:47 +01:00
André Cruz
aa4ebb07f8 Fix title. 2013-07-15 00:36:56 +01:00
André Cruz
4366d6a8c7 Fix test script, wtf! 2013-07-15 00:35:59 +01:00
André Cruz
aa76d49234 Improve README. 2013-07-15 00:34:38 +01:00
André Cruz
d9df06644e Initial commit. 2013-07-15 00:30:44 +01:00
André Cruz
554ee01263 Force camelCase. 2013-07-15 00:30:27 +01:00
André Cruz
b3055067d8 Force camelCase. 2013-07-15 00:30:18 +01:00
André Cruz
7f997d4b59 Force camelCase. 2013-07-15 00:30:08 +01:00
André Cruz
d2d959f455 Upgrade deps. 2013-07-14 17:21:45 +01:00
André Cruz
a9e497f878 Upgrade deps. 2013-07-14 17:21:29 +01:00
André Cruz
e423e9ffba Upgrade deps. 2013-07-14 17:21:17 +01:00
André Cruz
ff99fae928 Bump rc version. 2013-07-13 23:07:19 +01:00
André Cruz
3c9983ca12 Merge branch 'master' of github.com:bower/registry-client 2013-07-13 22:35:48 +01:00
André Cruz
d3412e7de6 Stupid typos, I was drunk when I did that. 2013-07-13 22:35:23 +01:00
Sven Lito
2841942899 more cache related tests 2013-07-11 10:33:47 +01:00
André Cruz
415e79b523 Bump rc version. 2013-07-10 20:39:30 +01:00
André Cruz
9a6fdaa42b Do not remove invalid files, throw instead. 2013-07-10 20:39:02 +01:00
Sven Lito
0cdbe998cb [tests] adds cache test 2013-07-10 17:14:24 +01:00
Sven Lito
35b0c49da5 clarify cache docs 2013-07-10 15:46:52 +01:00
Sven Lito
d43c9f006b fix module references 2013-07-10 15:01:18 +01:00
Sven Lito
01a6ae61d2 replace chai with expect.js 2013-07-10 14:53:48 +01:00
André Cruz
4b4b233377 Bump rc version. 2013-07-09 23:38:15 +01:00
André Cruz
ccc9907034 Change from bower_new to bower. 2013-07-09 23:36:15 +01:00
André Cruz
26520abe2b Merge branch 'master' of github.com:bower/json 2013-07-09 21:02:08 +01:00
André Cruz
1397c3248d Bump rc version. 2013-07-09 21:01:53 +01:00
André Cruz
c40dc39b88 Merge branch 'master' of github.com:bower/registry-client 2013-07-09 21:01:33 +01:00
André Cruz
a2753bb27d Bump rc version. 2013-07-09 21:01:19 +01:00
André Cruz
1c5529691b Bump rc version. 2013-07-09 21:00:58 +01:00
André Cruz
8e5bdc6b2b BC change. 2013-07-09 21:00:39 +01:00
André Cruz
39324d6b55 Merge pull request #6 from btford/master
Fix typos.
2013-07-08 11:18:57 -07:00
Brian Ford
b1ad187d1b Fix typos. 2013-07-08 11:12:40 -07:00
Sven Lito
606f15fec5 [tests] basic offline check 2013-07-08 14:23:55 +01:00
André Cruz
09a0eb26d1 Improve sentence. 2013-07-07 03:00:18 +01:00
André Cruz
bf9e8048ff Sort deps. 2013-07-07 02:51:55 +01:00
André Cruz
21ebc226e4 Sort deps. 2013-07-07 02:51:04 +01:00
André Cruz
8e3fef9096 Sort deps. 2013-07-07 02:50:47 +01:00
André Cruz
829dccc1b6 Bump rc version. 2013-07-07 02:38:53 +01:00
André Cruz
a65caa62b3 Add name validation. 2013-07-07 02:38:43 +01:00
André Cruz
ddb6f9c5b4 Merge branch 'master' of github.com:bower/config 2013-07-07 02:26:18 +01:00
André Cruz
e152ab6cf2 Use our own rc implementation. 2013-07-07 02:26:06 +01:00
André Cruz
4085af023a Typo. 2013-07-07 02:25:13 +01:00
André Cruz
686e401368 Add del. 2013-07-07 02:24:54 +01:00
André Cruz
2fdfa64b13 Typo. 2013-07-06 22:42:16 +01:00
André Cruz
f406cfcebb Change from toJson to toObject. 2013-07-06 22:40:37 +01:00
André Cruz
80f35725e6 Require relatively. 2013-07-06 22:31:05 +01:00
André Cruz
6741d99681 Fix rc version and update mocha. 2013-07-06 21:56:53 +01:00
André Cruz
ad4ec14778 Add editor config. 2013-07-06 21:55:53 +01:00
André Cruz
2bf16ad88b Fix rc name and update mocha. 2013-07-06 21:55:43 +01:00
André Cruz
6c56581c15 Typo. 2013-07-06 21:55:16 +01:00
André Cruz
ce89d9fbe0 Initial commit. 2013-07-06 21:53:04 +01:00
André Cruz
e298f74310 Improve sentence. 2013-07-06 14:55:43 +01:00
André Cruz
c550c1373e Merge branch 'master' of github.com:bower/registry-client 2013-07-06 14:39:33 +01:00
André Cruz
6a1bb88c3b Update README. 2013-07-06 14:39:24 +01:00
André Cruz
a585e96fdf Improve clear runtime cache. 2013-07-06 14:39:13 +01:00
Sven Lito
e78ef493a1 [tests] don't test for private methods 2013-07-06 14:28:25 +01:00
André Cruz
c139378694 Add comment. 2013-07-06 14:09:11 +01:00
André Cruz
d0eb3e760f Bump rc version. 2013-07-06 13:55:59 +01:00
André Cruz
9cfa3e5002 Changes to the reset & clear runtime cache.
Minor tweak to the grunt file.
2013-07-06 13:48:53 +01:00
Sven Lito
82278037ec [tests] adds comments and clearCache tests 2013-07-05 13:08:42 +01:00
Sven Lito
0b592f86d0 [tests] tidy up nock call 2013-07-05 12:50:25 +01:00
Sven Lito
ee4e003c2d [tests] fix method call 2013-07-05 12:50:08 +01:00
Sven Lito
b2904bc6fb [tests] mock search endpoint 2013-07-05 12:36:36 +01:00
Sven Lito
67b94cf52e sets nock as npm dependency 2013-07-05 11:53:08 +01:00
Sven Lito
c308d2c9dd adds nock fixture 2013-07-05 11:51:28 +01:00
Sven Lito
e36370b080 removes fixtures 2013-07-05 11:51:15 +01:00
Sven Lito
d0f005a6f1 adjust fixtures path 2013-07-05 10:54:51 +01:00
Sven Lito
a461fa9137 move fixture 2013-07-05 10:54:39 +01:00
Sven Lito
22e969fb59 fix npm module name
meh..
2013-07-05 10:52:25 +01:00
Sven Lito
34527c8395 [tests] fix breaking test 2013-07-05 10:49:53 +01:00
Sven Lito
012f4d68bc [wip] update tests 2013-07-05 10:46:48 +01:00
Sven Lito
724283433a adds lookup method tests 2013-07-04 15:55:10 +01:00
Sven Lito
aea19b93b1 rename client -> registry
because that's what it is right?
2013-07-04 15:11:44 +01:00
Sven Lito
73020a711d tweaking docs 2013-07-04 15:00:15 +01:00
Sven Lito
7e62e671e3 updates testrunner 2013-07-04 14:51:29 +01:00
Sven Lito
2e845ac0ab adds createError tests 2013-07-04 14:51:17 +01:00
Sven Lito
2df41e52ed adds Cache tests 2013-07-04 14:51:09 +01:00
Sven Lito
bf8e93f581 adds search tests 2013-07-04 14:51:03 +01:00
Sven Lito
f5eec3283c adds register tests 2013-07-04 14:50:54 +01:00
Sven Lito
223161c7d6 adds list tests 2013-07-04 14:50:47 +01:00
Sven Lito
b8ba6e4827 adds index tests 2013-07-04 14:50:39 +01:00
Sven Lito
15c8259ac2 adds lookup tests 2013-07-04 14:50:32 +01:00
Sven Lito
720492932f update Client.js tests 2013-07-04 10:40:36 +01:00
Sven Lito
a08a0fb084 add tests to jshint 2013-07-04 10:40:21 +01:00
Sven Lito
827fbaac1f update jshint settings 2013-07-04 10:40:03 +01:00
André Cruz
297224bd31 Merge branch 'master' of github.com:bower/registry-client 2013-07-03 16:20:38 +01:00
André Cruz
9dc835c60c Bump rc version. 2013-07-03 16:18:50 +01:00
André Cruz
67a96bc2f3 Typo ignoring ENOENT errors. 2013-07-03 16:18:22 +01:00
Sven Lito
ff5bf16111 fix file reference 2013-07-02 23:33:52 +01:00
Sven Lito
8d22059462 adds grunt travis setup 2013-07-02 23:26:07 +01:00
Sven Lito
91d144f5f7 Merge branch 'master' of https://github.com/bower/registry-client 2013-07-02 22:51:50 +01:00
Sven Lito
c894b1d335 adds grunt and test runner setup 2013-07-02 22:51:43 +01:00
Sven Lito
d30a7bf6a1 adds .editorconfig 2013-07-02 22:50:26 +01:00
André Cruz
649d5f56c9 Minor doc typo. 2013-07-02 09:41:17 +01:00
André Cruz
36b033c2f7 Switch to graceful-fs. 2013-07-01 20:28:37 +01:00
André Cruz
dc9bce915b Switch to graceful-fs. 2013-07-01 20:28:11 +01:00
André Cruz
c9a7cfafd0 Bump rc version! 2013-06-28 21:59:56 +01:00
André Cruz
9d9585ecba Ignore ENOENT erros when deleting from the cache. 2013-06-28 21:59:24 +01:00
André Cruz
0ebd7e6a58 Small typo in README. 2013-06-24 14:51:54 +01:00
André Cruz
a100abc3b6 Bump rc version. 2013-06-24 14:48:36 +01:00
André Cruz
94f6945d5a Complete README, change cleanRuntimeCache to clearRuntimeCache. 2013-06-24 14:48:20 +01:00
André Cruz
442d771a7a Bump rc version. 2013-06-23 17:44:29 +01:00
André Cruz
242e11eefc Add register. 2013-06-23 17:43:52 +01:00
André Cruz
7fbbae8cd4 Bump (rc3). 2013-06-22 17:05:13 +01:00
André Cruz
b80d96d9c7 Fix tests. 2013-06-22 14:15:35 +01:00
André Cruz
d8e69360b9 Add search command, other fixes/changes.
- Use LRU to prevent huge memory usage when using the module in long-living programs
- Lookup no longer throws when a package is not found (null is returned instead).
- Cache mechanism improvements.
- Misc fixes.
2013-06-22 14:11:11 +01:00
André Cruz
83ae9b66a3 Add license note, misc tweaks. 2013-06-22 14:07:51 +01:00
André Cruz
4ac052c10b Oops. 2013-05-26 13:17:54 +01:00
André Cruz
f1717f8319 Move force and offline options to the constructor. 2013-05-26 13:12:54 +01:00
André Cruz
cb000549bd Small tweaks. 2013-05-26 12:46:45 +01:00
André Cruz
2712aa2ae2 Merge branch 'master' of github.com:bower/json 2013-05-24 22:55:03 +01:00
André Cruz
7dcefa6bee Add repository to package.json and fix URL's, also bump rc version. 2013-05-24 22:54:41 +01:00
André Cruz
70e3528809 Add repository to package.json and bump rc version. 2013-05-24 22:53:52 +01:00
André Cruz
c7780a2708 Try next registry endpoint on 404. 2013-05-24 22:53:17 +01:00
André Cruz
26bab84d04 Fix parse usage in README. 2013-05-19 14:27:36 +02:00
André Cruz
efea6136e7 Merge pull request #2 from badunk/find-readme
Quick fix for bowerJson.find() example
2013-05-19 05:26:42 -07:00
Duncan Wong
c56026c18a quick fix for bowerJson.find() example 2013-05-18 19:26:49 -06:00
André Cruz
1d9504d0f0 Bump rc. 2013-05-17 13:14:53 +01:00
André Cruz
780b1f8acc Fix various bugs. 2013-05-17 13:13:47 +01:00
André Cruz
9b6c5741de Move cache did creation to appropriate class, small pert improvement.. 2013-05-13 11:10:10 +01:00
André Cruz
0742e18edd Oops. 2013-05-11 14:00:11 +01:00
André Cruz
cb59c2489b Change main file. 2013-05-11 13:56:48 +01:00
André Cruz
3158b544d5 Minor lang change. 2013-05-11 13:42:23 +01:00
André Cruz
e80270d1fa Add correct maxAge for the cache entries. 2013-05-11 13:40:59 +01:00
André Cruz
99b37f24bb Slightly different strategy, implement lookup cache. 2013-05-11 13:33:40 +01:00
André Cruz
fdbdcc4130 More doc typos. 2013-05-10 20:01:35 +01:00
André Cruz
08104966b2 Improve doc. 2013-05-10 19:54:30 +01:00
André Cruz
1c99133177 Change package to bower-registry-client.
Some changes to the codebase.
2013-05-10 19:46:52 +01:00
André Cruz
28780dc67e Wrong async usage of doUntil. 2013-05-09 21:15:49 +01:00
André Cruz
423ce54d8a Add empty tests and add cache TODO. 2013-05-09 13:18:59 +01:00
André Cruz
73bab73db6 Initial implementation of lookup. 2013-05-09 13:11:57 +01:00
André Cruz
3e90471fa2 Setup project. 2013-05-07 17:38:53 +01:00
André Cruz
7f2db8a9a7 Setup project. 2013-05-07 17:30:29 +01:00
André Cruz
d5fc147ffd Merge pull request #1 from dylang/human-friendly-error-message
Made the error message more understandable.
2013-05-07 09:29:32 -07:00
André Cruz
77ffd7bbf9 Merge branch 'master' of github.com:bower/bower-json 2013-05-07 17:26:32 +01:00
André Cruz
42775d3477 Improve gitignore. 2013-05-07 17:26:22 +01:00
André Cruz
6932b8c378 Better license format. 2013-05-07 17:26:10 +01:00
Dylan Greene
cff641ef80 add quotes around the path and fix the test 2013-05-06 22:16:52 -04:00
André Cruz
373abf1b24 Update README.md 2013-05-06 23:11:51 +02:00
Dylan Greene
f668596667 Made the error message more understandable. 2013-05-06 17:02:57 -04:00
André Cruz
93e0e815d7 Fix indentation. 2013-05-05 03:03:56 +01:00
André Cruz
05275c8938 Improve README. 2013-05-05 04:02:15 +02:00
André Cruz
19475db7dd Typos. 2013-05-05 04:00:55 +02:00
André Cruz
aa315d7c97 Merge branch 'master' of github.com:bower/bower-json 2013-04-23 23:58:48 +01:00
André Cruz
080b25e30c Update .gitignore. 2013-04-23 23:58:35 +01:00
André Cruz
a7c8c08183 Update README.md 2013-04-23 10:12:47 +02:00
André Cruz
bb98627d2b Made parse async for consistency, tweak error codes. 2013-04-20 12:17:00 +01:00
André Cruz
3060866586 Add test for invalid json. 2013-04-20 11:37:15 +01:00
André Cruz
eacf121f78 Update README. 2013-04-20 11:23:24 +01:00
André Cruz
c39535fdd0 Merge branch 'master' of github.com:bower/bower-json 2013-04-20 11:21:31 +01:00
André Cruz
c3311df2a8 Initial implementation and tests. 2013-04-20 11:20:41 +01:00
André Cruz
5a17314b2c Oops. 2013-04-19 22:00:24 +02:00
André Cruz
07281f050c Change to just bower-json. 2013-04-19 13:58:18 +01:00
André Cruz
9c3757fb0c Doc improv. 2013-04-19 12:33:07 +01:00
André Cruz
f5d5e59040 Typo. 2013-04-19 12:31:04 +01:00
André Cruz
70880c066f Add LICENSE and README. 2013-04-19 12:29:20 +01:00
André Cruz
5508c70f3a Setup module. 2013-04-19 11:19:47 +01:00
208 changed files with 24409 additions and 10437 deletions

View File

@@ -15,5 +15,4 @@ trim_trailing_whitespace = false
insert_final_newline = false
[{package,bower}.json]
indent_style = space
indent_size = 2

6
.eslintignore Normal file
View File

@@ -0,0 +1,6 @@
node_modules
test/assets
test/reports
test/sample
test/tmp
packages/bower-logger/test

View File

@@ -17,9 +17,8 @@
"strict": 0,
"semi": 0,
"comma-spacing": 2,
"quote-props": [2, "consistent", { "keywords": true }],
"quote-props": [2, "as-needed"],
"quotes": [2, "single", "avoid-escape"],
"indent": [2, 4],
"no-cond-assign": [ 2, "except-parens" ],
"no-debugger": 2,
"no-dupe-args": 2,
@@ -41,7 +40,7 @@
"no-new-wrappers": 2,
"no-invalid-this": 0,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
"space-before-function-paren": [2, "never"],
"space-infix-ops": 2,
"keyword-spacing": 2,
"new-parens": 2,

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
open_collective: bower

44
.github/ISSUE_TEMPLATE.md vendored Normal file
View 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
View File

@@ -0,0 +1,62 @@
name: build
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
test:
name: Node v${{ matrix.node-version }} on ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# https://github.com/actions/setup-node/issues/27
node-version: [0.10.x, 0.12.x, 4.x, 6.x, 8.x, 10.x, 12.x, 14.x]
os: [ubuntu-latest, macOS-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Set git config
shell: bash
run: |
git config --global core.autocrlf false
git config --global core.symlinks true
if: runner.os == 'Windows'
- uses: actions/checkout@v2
- name: install
run: yarn
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: lint
run: npm run lint
- name: bower tests
run: npm test
env:
CI: true
- name: bower-logger tests
run: (cd packages/bower-logger && npm install && npm test)
env:
CI: true
- name: bower-config tests
run: (cd packages/bower-config && npm install && npm test)
env:
CI: true
- name: bower-endpoint-parser tests
run: (cd packages/bower-endpoint-parser && npm install && npm test)
env:
CI: true
- name: bower-json tests
run: (cd packages/bower-json && npm install && npm test)
env:
CI: true
- name: bower-registry-client tests
run: (cd packages/bower-registry-client && npm install && npm test)
env:
CI: true

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
!lib/bin
/node_modules
/npm-debug.log

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
**/node_modules/**
**/test/assets/**
**/bower_components/**
test/sample

View File

@@ -1,31 +0,0 @@
sudo: false
env:
- NODE_VERSION=0.10
- NODE_VERSION=0.11
- NODE_VERSION=0.12
- NODE_VERSION=4.0
- NODE_VERSION=5.0
install:
- test $TRAVIS_OS_NAME = "osx" && brew install nvm && source $(brew --prefix nvm)/nvm.sh || test $TRAVIS_OS_NAME = "linux"
- nvm install $NODE_VERSION
- npm install -g npm@^2.0.0
- node --version
- npm --version
- git --version
- svn --version | head -n 1
- npm install -g grunt-cli
- npm install
os:
- osx
- linux
matrix:
allow_failures:
- os: osx
- env: "NODE_VERSION=0.11"
script:
- grunt travis

View File

@@ -1,6 +1,33 @@
# Changelog
## 1.7.8 - 2016-01-04
## Newer releases
Please see: https://github.com/bower/bower/releases
## 1.8.0 - 2016-11-07
- Download tar archives from GitHub when possible (#2263)
- Change default shorthand resolver for github from `git://` to `https://`
- Fix ssl handling by not setting GIT_SSL_NO_VERIFY=false (#2361)
- Allow for removing components with url instead of name (#2368)
- Show in warning message location of malformed bower.json (#2357)
- Improve handling of non-semver versions in git resolver (#2316)
- Fix handling of cached releases pluginResolverFactory (#2356)
- Allow to type the entire version when conflict occured (#2243)
- Allow `owner/reponame` shorthand for registering components (#2248)
- Allow single-char repo names and package names (#2249)
- Make `bower version` no longer honor `version` in bower.json (#2232)
- Add `postinstall` hook (#2252)
- Allow for `@` instead of `#` for `install` and `info` commands (#2322)
- Upgrade all bundled modules
## 1.7.9 - 2016-04-05
- Show warnings for invalid bower.json fields
- Update bower-json
- Less strict validation on package name (allow spaces, slashes, and "@")
## 1.7.8 - 2016-04-04
- Don't ask for git credentials in non-interactive session, fixes #956 #1009
- Prevent swallowing exceptions with programmatic api, fixes #2187
@@ -11,6 +38,8 @@
- Update opn package to fix issues with "bower open" command on Windows
- Update bower-config
- Do not interpolate environment variables in script hooks, fixes bower/config#47
- Update bower-json
- Validate package name more strictly and allow only latin letters, dots, dashes and underscores
- Add support for "save" and "save-exact" in .bowerrc, #2161
## 1.7.7 - 2016-01-27
@@ -75,7 +104,7 @@ https://github.com/npm/npm/issues/11227
- Update bower config
- Loads the .bowerrc file from the cwd specified on the command line
- Allow the use of environment variables in .bowerrc ([#41](https://github.com/bower/config/issues/41))
- Allow for array notation in ENV variables ([#44](https://github.com/bower/config/issues/44))
- Allow for array notation in ENV variables ([#44](https://github.com/bower/config/issues/44))
## 1.6.9 - 2015-12-04

View File

@@ -18,7 +18,7 @@ Bower is a large community project with many different developers contributing a
## Team Meetings
We communicate through a channel on slack: https://gitter.im/bower
We communicate through a channel on Discord https://discord.gg/0fFM7QF0KpZRh2cY
If you'd like to attend the meetings, please fill the [support form](http://goo.gl/forms/P1ndzCNoiG), and you'll get an invite.
@@ -29,9 +29,9 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
requests](#pull-requests), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests. Use
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower)
[Gitter Channel](https://gitter.im/bower/bower)
[Mailing List](http://groups.google.com/group/twitter-bower)
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower),
[Discord Channel](https://discordapp.com/channels/119103197720739842/123728452816732160),
[Mailing List](http://groups.google.com/group/twitter-bower),
(twitter-bower@googlegroups.com), or
[#bower](http://webchat.freenode.net/?channels=bower) on Freenode.

View File

@@ -1,213 +0,0 @@
'use strict';
var tmp = require('tmp');
var childProcess = require('child_process');
var arraydiff = require('arr-diff');
var fs = require('fs');
var wrench = require('wrench');
var inquirer = require('inquirer');
var path = require('path');
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
eslint: {
options: {
fix: true
},
files: [
'Gruntfile.js',
'bin/*',
'lib/**/*.js',
'test/**/*.js',
'!test/assets/**/*',
'!test/reports/**/*',
'!test/sample/**/*',
'!test/tmp/**/*'
]
},
simplemocha: {
options: {
reporter: 'spec',
timeout: '15000'
},
full: {
src: ['test/test.js']
},
short: {
options: {
reporter: 'dot'
},
src: ['test/test.js']
}
},
exec: {
'assets': {
command: 'node test/packages.js && node test/packages-svn.js'
},
'assets-force': {
command: 'node test/packages.js --force && node test/packages-svn.js --force'
},
'cover': {
command: 'node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- --timeout 30000 -R dot test/test.js'
},
'coveralls': {
command: 'npm run coveralls < test/reports/lcov.info',
exitCodes: [0, 1, 2, 3] // Alow for failure for coverage report
}
},
watch: {
files: ['<%= eslint.files %>'],
tasks: ['eslint', 'simplemocha:short']
}
});
grunt.registerTask('assets', ['exec:assets-force']);
grunt.registerTask('test', ['eslint', 'exec:assets', 'simplemocha:full']);
grunt.registerTask('cover', 'exec:cover');
grunt.registerTask('travis', ['eslint', 'exec:assets', 'exec:cover', 'exec:coveralls']);
grunt.registerTask('default', 'test');
grunt.task.registerTask('publish', 'Perform final checks and publish Bower', function () {
var npmVersion = JSON.parse(childProcess.execSync('npm version --json').toString()).npm.split('.');
var npmMajor = parseInt(npmVersion[0], 10);
var npmMinor = parseInt(npmVersion[1], 10);
var jsonPackage = require('./package');
if (npmMajor !== 3 || npmMinor < 5) {
grunt.log.writeln('You need to use at least npm@3.5 to publish bower.');
grunt.log.writeln('It is because npm 2.x produces too long paths that Windows does not handle.');
grunt.log.writeln('Please upgrade it: npm install -g npm');
process.exit(1);
}
var version = jsonPackage.version;
var changelog = fs.readFileSync('./CHANGELOG.md');
if (changelog.indexOf('## ' + version) === -1) {
grunt.log.writeln('Please add changelog.md entry for this bower version (' + version + ')');
var lastRelease = childProcess.execSync('git tag | tail -1').toString().trim();
grunt.log.writeln('Commits since last release (' + lastRelease + '): \n');
grunt.log.writeln(childProcess.execSync('git log --oneline ' + lastRelease + '..').toString());
process.exit(1);
}
if (childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim() !== 'master') {
grunt.log.writeln('You need to release bower from the "master" branch');
process.exit(1);
}
if (process.env.SKIP_TESTS !== '1') {
grunt.log.writeln('Reinstalling dependencies...');
childProcess.execSync('rm -rf node_modules && npm install', { stdio: [0, 1, 2] });
grunt.log.writeln('Running test suite...');
childProcess.execSync('grunt test', { stdio: [0, 1, 2] });
}
var dir = tmp.dirSync().name;
wrench.copyDirSyncRecursive(__dirname, dir, {
forceDelete: true,
include: function (path) {
return !path.match(/node_modules|\.git|test/);
}
});
grunt.log.writeln('Installing production dependencies...');
childProcess.execSync('npm install --production --silent', { cwd: dir, stdio: [0, 1, 2] });
delete jsonPackage.dependencies;
delete jsonPackage.devDependencies;
delete jsonPackage.scripts;
fs.writeFileSync(path.resolve(dir, 'package.json'), JSON.stringify(jsonPackage, null, ' ') + '\n');
grunt.log.writeln('Moving node_modules to lib directory...');
wrench.copyDirSyncRecursive(path.resolve(dir, 'node_modules'), path.resolve(dir, 'lib', 'node_modules'));
wrench.rmdirSyncRecursive(path.resolve(dir, 'node_modules'));
grunt.log.writeln('Testing bower on sample project...');
childProcess.execSync(
'cd test/sample && rm -rf bower_components && ' + dir + '/bin/bower install --force', { stdio: [0, 1, 2] }
);
var expectedPackages = (
'SHA-1 ace-builds almond angular angular-animate angular-bootstrap angular-charts angular-contenteditable ' +
'angular-deckgrid angular-fullscreen angular-gravatar angular-hotkeys angular-local-storage angular-marked ' +
'angular-moment angular-sanitize angular-touch angular-ui-router angular-ui-sortable ' +
'angulartics asEvented bootstrap coffee-script d3 es6-shim font-awesome howler jquery ' +
'jquery-ui jquery-waypoints js-beautify lodash lz-string marked moment ng-file-upload peerjs ' +
'requirejs restangular slimScroll slimScrollHorizontal venturocket-angular-slider'
).split(' ');
var installedPackages = fs.readdirSync('./test/sample/bower_components');
var installedDiff = arraydiff(expectedPackages, installedPackages);
if (installedDiff.length > 0) {
grunt.log.writeln('ERROR. Some packages were not installed by bower: ');
grunt.log.writeln(installedDiff.join(', '));
process.exit(1);
}
grunt.log.writeln('\nBower production bundle installed in:');
grunt.log.writeln(dir + '\n');
var questions = [
{
type: 'confirm',
name: 'review',
message: 'Did you review all the changes with "git diff"?',
default: false
},
{
type: 'confirm',
name: 'changelog',
message: 'Are you sure the CHANGELOG.md contains all changes?',
default: false
},
{
type: 'confirm',
name: 'tests',
message: 'Are you sure all tests are passing on Travis and Appveyor?',
default: false
},
{
type: 'confirm',
name: 'publish',
message: 'Are you SURE you want to publish ' + jsonPackage.name + '@' + jsonPackage.version + '?',
default: false
}
];
var done = this.async();
inquirer.prompt(questions, function (answers) {
if (!answers.review || !answers.changelog || !answers.tests || !answers.publish) {
grunt.log.writeln('Please publish bower after you fix this issue');
process.exit(1);
}
grunt.log.writeln('\nPlease remember to tag this relese, and add a release on Github!');
grunt.log.writeln('\nAlso, please remember to test published Bower one more time!');
grunt.log.writeln('\nPublishing Bower...');
childProcess.execSync('npm publish --tag beta', { cwd: dir, stdio: [0, 1, 2] });
done();
});
});
};

View File

@@ -1,4 +1,4 @@
Copyright (c) 2016 Twitter and other contributors
Copyright (c) 2013-present Twitter and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

121
README.md
View File

@@ -1,13 +1,10 @@
# Bower - A package manager for the web
> Bower needs resources for its maintenance. Please fill [this form](https://docs.google.com/forms/d/1i-Opb-uPdqUBBZQSbngv3Y3bfolG1gbBvtRLfxMnzRE/viewform?c=0&w=1) if you think you can help.
[![Build](https://github.com/bower/bower/workflows/build/badge.svg)](https://github.com/bower/bower/actions?query=branch%3Amaster)
[![Backers on Open Collective](https://opencollective.com/bower/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/bower/sponsors/badge.svg)](#sponsors)
[![Build Status](https://travis-ci.org/bower/bower.svg?branch=master)](https://travis-ci.org/bower/bower)
[![Windows Build](https://ci.appveyor.com/api/projects/status/jr6vfra8w84plh2g/branch/master?svg=true)](https://ci.appveyor.com/project/sheerun/bower/history)
[![Coverage Status](https://img.shields.io/coveralls/bower/bower.svg)](https://coveralls.io/r/bower/bower?branch=master)
[![Discord chat](https://img.shields.io/badge/discord-join%20chat%20%E2%86%92-brightgreen.svg?style=flat)](https://discord.gg/0fFM7QF0KpZRh2cY)
[![Issue Stats](http://issuestats.com/github/bower/bower/badge/pr?style=flat)](http://issuestats.com/github/bower/bower)
[![Issue Stats](http://issuestats.com/github/bower/bower/badge/issue?style=flat)](http://issuestats.com/github/bower/bower)
> ..psst! While Bower is maintained, we recommend [yarn](https://yarnpkg.com/) and [webpack](https://webpack.js.org/) or [parcel](https://parceljs.org/) for new front-end projects!
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
@@ -119,8 +116,116 @@ Note that on Windows for tests to pass you need to configure Git before cloning:
git config --global core.autocrlf input
```
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/bower#backer)]
<a href="https://opencollective.com/bower/backer/0/website" target="_blank"><img src="https://opencollective.com/bower/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/1/website" target="_blank"><img src="https://opencollective.com/bower/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/2/website" target="_blank"><img src="https://opencollective.com/bower/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/3/website" target="_blank"><img src="https://opencollective.com/bower/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/4/website" target="_blank"><img src="https://opencollective.com/bower/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/5/website" target="_blank"><img src="https://opencollective.com/bower/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/6/website" target="_blank"><img src="https://opencollective.com/bower/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/7/website" target="_blank"><img src="https://opencollective.com/bower/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/8/website" target="_blank"><img src="https://opencollective.com/bower/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/9/website" target="_blank"><img src="https://opencollective.com/bower/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/10/website" target="_blank"><img src="https://opencollective.com/bower/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/11/website" target="_blank"><img src="https://opencollective.com/bower/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/12/website" target="_blank"><img src="https://opencollective.com/bower/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/13/website" target="_blank"><img src="https://opencollective.com/bower/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/14/website" target="_blank"><img src="https://opencollective.com/bower/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/15/website" target="_blank"><img src="https://opencollective.com/bower/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/16/website" target="_blank"><img src="https://opencollective.com/bower/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/17/website" target="_blank"><img src="https://opencollective.com/bower/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/18/website" target="_blank"><img src="https://opencollective.com/bower/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/19/website" target="_blank"><img src="https://opencollective.com/bower/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/20/website" target="_blank"><img src="https://opencollective.com/bower/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/21/website" target="_blank"><img src="https://opencollective.com/bower/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/22/website" target="_blank"><img src="https://opencollective.com/bower/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/23/website" target="_blank"><img src="https://opencollective.com/bower/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/24/website" target="_blank"><img src="https://opencollective.com/bower/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/25/website" target="_blank"><img src="https://opencollective.com/bower/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/26/website" target="_blank"><img src="https://opencollective.com/bower/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/27/website" target="_blank"><img src="https://opencollective.com/bower/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/28/website" target="_blank"><img src="https://opencollective.com/bower/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/29/website" target="_blank"><img src="https://opencollective.com/bower/backer/29/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/30/website" target="_blank"><img src="https://opencollective.com/bower/backer/30/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/31/website" target="_blank"><img src="https://opencollective.com/bower/backer/31/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/32/website" target="_blank"><img src="https://opencollective.com/bower/backer/32/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/33/website" target="_blank"><img src="https://opencollective.com/bower/backer/33/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/34/website" target="_blank"><img src="https://opencollective.com/bower/backer/34/avatar.svg"></a>
<a href="https://opencollective.com/bower/backer/35/website" target="_blank"><img src="https://opencollective.com/bower/backer/35/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/bower#sponsor)]
<a href="https://opencollective.com/bower/sponsor/0/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/1/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/2/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/3/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/4/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/5/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/6/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/7/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/8/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/9/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/10/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/11/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/12/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/13/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/14/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/15/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/16/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/17/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/18/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/19/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/20/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/21/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/22/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/23/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/24/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/25/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/26/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/27/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/28/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/29/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/29/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/30/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/30/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/31/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/31/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/32/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/32/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/33/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/33/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/34/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/34/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/35/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/35/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/36/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/36/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/37/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/37/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/38/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/38/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/39/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/39/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/40/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/40/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/41/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/41/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/42/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/42/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/43/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/43/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/44/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/44/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/45/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/45/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/46/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/46/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/47/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/47/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/48/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/48/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/49/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/49/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/50/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/50/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/51/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/51/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/52/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/52/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/53/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/53/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/54/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/54/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/55/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/55/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/56/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/56/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/57/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/57/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/58/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/58/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/59/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/59/avatar.svg"></a>
<a href="https://opencollective.com/bower/sponsor/60/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/60/avatar.svg"></a>
## License
Copyright (c) 2016 Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
Copyright (c) 2012-present Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
Licensed under the MIT License

View File

@@ -1,43 +0,0 @@
# Thanks for Grunt for template of this file!
# http://www.appveyor.com/docs/appveyor-yml
# Fix line endings in Windows. (runs before repo cloning)
init:
- git config --global core.autocrlf input
# Test against these versions of Node.js.
environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "5"
- nodejs_version: "4"
# Allow failing jobs for bleeding-edge Node.js versions.
matrix:
allow_failures:
- nodejs_version: "0.10"
- nodejs_version: "5"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node 0.STABLE.latest
- ps: Install-Product node $env:nodejs_version
# Output useful info for debugging.
- node --version
- npm --version
- git --version
- svn --version
# Install all dependencies
- npm install
# Post-install test scripts.
test_script:
- cmd: npm run ci
# Don't actually build.
build: off
# Set build version format here instead of in the admin panel.
version: "{build}"

View File

@@ -18,8 +18,8 @@ var logger;
var levels = Logger.LEVELS;
options = cli.readOptions({
'version': { type: Boolean, shorthand: 'v' },
'help': { type: Boolean, shorthand: 'h' },
version: { type: Boolean, shorthand: 'v' },
help: { type: Boolean, shorthand: 'h' },
'allow-root': { type: Boolean }
});
@@ -73,12 +73,12 @@ command = command && command.replace(/\./g, ' ');
if (!commandFunc) {
logger = bower.commands.help();
command = 'help';
// If the user requested help, show the command's help
// Do the same if the actual command is a group of other commands (e.g.: cache)
// If the user requested help, show the command's help
// Do the same if the actual command is a group of other commands (e.g.: cache)
} else if (options.help || !commandFunc.line) {
logger = bower.commands.help(command);
command = 'help';
// Call the line method
// Call the line method
} else {
logger = commandFunc.line(process.argv);
@@ -95,42 +95,47 @@ renderer = cli.getRenderer(command, logger.json, bower.config);
function handleLogger(logger, renderer) {
logger
.on('end', function (data) {
if (!bower.config.silent && !bower.config.quiet) {
renderer.end(data);
}
})
.on('error', function (err) {
if (command !== 'help' && err.code === 'EREADOPTIONS') {
logger = bower.commands.help(command);
renderer = cli.getRenderer('help', logger.json, bower.config);
handleLogger(logger, renderer);
} else {
if (levels.error >= loglevel) {
renderer.error(err);
.on('end', function(data) {
if (!bower.config.silent && !bower.config.quiet) {
renderer.end(data);
}
})
.on('error', function(err) {
if (
command !== 'help' &&
(err.code === 'EREADOPTIONS' || err.code === 'EINVFORMAT')
) {
logger = bower.commands.help(command);
renderer = cli.getRenderer('help', logger.json, bower.config);
handleLogger(logger, renderer);
} else {
if (levels.error >= loglevel) {
renderer.error(err);
}
process.exit(1);
}
})
.on('log', function (log) {
if (levels[log.level] >= loglevel) {
renderer.log(log);
}
})
.on('prompt', function (prompt, callback) {
renderer.prompt(prompt)
.then(function (answer) {
callback(answer);
process.exit(1);
}
})
.on('log', function(log) {
if (levels[log.level] >= loglevel) {
renderer.log(log);
}
})
.on('prompt', function(prompt, callback) {
renderer.prompt(prompt).then(function(answer) {
callback(answer);
});
});
});
}
handleLogger(logger, renderer);
// Warn if HOME is not SET
if (!userHome) {
logger.warn('no-home', 'HOME environment variable not set. User config will not be loaded.');
logger.warn(
'no-home',
'HOME environment variable not set. User config will not be loaded.'
);
}
if (bower.config.interactive) {

View File

@@ -22,10 +22,10 @@ function clean(logger, endpoints, options, config) {
// Generate decomposed endpoints and names based on the endpoints
if (endpoints) {
decEndpoints = endpoints.map(function (endpoint) {
decEndpoints = endpoints.map(function(endpoint) {
return endpointParser.decompose(endpoint);
});
names = decEndpoints.map(function (decEndpoint) {
names = decEndpoints.map(function(decEndpoint) {
return decEndpoint.name || decEndpoint.source;
});
}
@@ -33,27 +33,26 @@ function clean(logger, endpoints, options, config) {
return Q.all([
clearPackages(decEndpoints, config, logger),
clearLinks(names, config, logger)
])
.spread(function (entries) {
]).spread(function(entries) {
return entries;
});
}
function clearPackages(decEndpoints, config, logger) {
var repository = new PackageRepository(config, logger);
var repository = new PackageRepository(config, logger);
return repository.list()
.then(function (entries) {
return repository.list().then(function(entries) {
var promises;
// Filter entries according to the specified packages
if (decEndpoints) {
entries = entries.filter(function (entry) {
return !!mout.array.find(decEndpoints, function (decEndpoint) {
entries = entries.filter(function(entry) {
return !!mout.array.find(decEndpoints, function(decEndpoint) {
var entryPkgMeta = entry.pkgMeta;
// Check if name or source match the entry
if (decEndpoint.name !== entryPkgMeta.name &&
if (
decEndpoint.name !== entryPkgMeta.name &&
decEndpoint.source !== entryPkgMeta.name &&
decEndpoint.source !== entryPkgMeta._source
) {
@@ -67,36 +66,47 @@ function clearPackages(decEndpoints, config, logger) {
// If it's a semver target, compare using semver spec
if (semver.validRange(decEndpoint.target)) {
return semver.satisfies(entryPkgMeta.version, decEndpoint.target);
return semver.satisfies(
entryPkgMeta.version,
decEndpoint.target
);
}
// Otherwise, compare against target/release
return decEndpoint.target === entryPkgMeta._target ||
decEndpoint.target === entryPkgMeta._release;
return (
decEndpoint.target === entryPkgMeta._target ||
decEndpoint.target === entryPkgMeta._release
);
});
});
}
promises = entries.map(function (entry) {
return repository.eliminate(entry.pkgMeta)
.then(function () {
logger.info('deleted', 'Cached package ' + entry.pkgMeta.name + ': ' + entry.canonicalDir, {
file: entry.canonicalDir
});
promises = entries.map(function(entry) {
return repository.eliminate(entry.pkgMeta).then(function() {
logger.info(
'deleted',
'Cached package ' +
entry.pkgMeta.name +
': ' +
entry.canonicalDir,
{
file: entry.canonicalDir
}
);
});
});
return Q.all(promises)
.then(function () {
if (!decEndpoints) {
// Ensure that everything is cleaned,
// even invalid packages in the cache
return repository.clear();
}
})
.then(function () {
return entries;
});
.then(function() {
if (!decEndpoints) {
// Ensure that everything is cleaned,
// even invalid packages in the cache
return repository.clear();
}
})
.then(function() {
return entries;
});
});
}
@@ -106,59 +116,59 @@ function clearLinks(names, config, logger) {
// If no names are passed, grab all links
if (!names) {
promise = Q.nfcall(fs.readdir, dir)
.fail(function (err) {
promise = Q.nfcall(fs.readdir, dir).fail(function(err) {
if (err.code === 'ENOENT') {
return [];
}
throw err;
});
// Otherwise use passed ones
// Otherwise use passed ones
} else {
promise = Q.resolve(names);
}
return promise
.then(function (names) {
return promise.then(function(names) {
var promises;
var linksToRemove = [];
// Decide which links to delete
promises = names.map(function (name) {
promises = names.map(function(name) {
var link = path.join(config.storage.links, name);
return Q.nfcall(fs.readlink, link)
.then(function (linkTarget) {
// Link exists, check if it points to a folder
// that still exists
return Q.nfcall(fs.stat, linkTarget)
.then(function (stat) {
// Target is not a folder..
if (!stat.isDirectory()) {
return Q.nfcall(fs.readlink, link).then(
function(linkTarget) {
// Link exists, check if it points to a folder
// that still exists
return (
Q.nfcall(fs.stat, linkTarget)
.then(function(stat) {
// Target is not a folder..
if (!stat.isDirectory()) {
linksToRemove.push(link);
}
})
// Error occurred reading the link
.fail(function() {
linksToRemove.push(link);
})
);
// Ignore if link does not exist
},
function(err) {
if (err.code !== 'ENOENT') {
linksToRemove.push(link);
}
})
// Error occurred reading the link
.fail(function () {
linksToRemove.push(link);
});
// Ignore if link does not exist
}, function (err) {
if (err.code !== 'ENOENT') {
linksToRemove.push(link);
}
});
);
});
return Q.all(promises)
.then(function () {
return Q.all(promises).then(function() {
var promises;
// Remove each link that was declared as invalid
promises = linksToRemove.map(function (link) {
return Q.nfcall(rimraf, link)
.then(function () {
promises = linksToRemove.map(function(link) {
return Q.nfcall(rimraf, link).then(function() {
logger.info('deleted', 'Invalid link: ' + link, {
file: link
});
@@ -172,7 +182,7 @@ function clearLinks(names, config, logger) {
// -------------------
clean.readOptions = function (argv) {
clean.readOptions = function(argv) {
var cli = require('../../util/cli');
var options = cli.readOptions(argv);
var endpoints = options.argv.remain.slice(2);

View File

@@ -13,12 +13,11 @@ function list(logger, packages, options, config) {
packages = null;
}
return repository.list()
.then(function (entries) {
return repository.list().then(function(entries) {
if (packages) {
// Filter entries according to the specified packages
entries = entries.filter(function (entry) {
return !!mout.array.find(packages, function (pkg) {
entries = entries.filter(function(entry) {
return !!mout.array.find(packages, function(pkg) {
return pkg === entry.pkgMeta.name;
});
});
@@ -30,7 +29,7 @@ function list(logger, packages, options, config) {
// -------------------
list.readOptions = function (argv) {
list.readOptions = function(argv) {
var cli = require('../../util/cli');
var options = cli.readOptions(argv);
var packages = options.argv.remain.slice(2);

View File

@@ -7,15 +7,17 @@ function help(logger, name, config) {
var json;
if (name) {
json = path.resolve(__dirname, '../templates/json/help-' + name.replace(/\s+/g, '/') + '.json');
json = path.resolve(
__dirname,
'../templates/json/help-' + name.replace(/\s+/g, '/') + '.json'
);
} else {
json = path.resolve(__dirname, '../templates/json/help.json');
}
return Q.promise(function (resolve) {
return Q.promise(function(resolve) {
fs.exists(json, resolve);
})
.then(function (exists) {
}).then(function(exists) {
if (!exists) {
throw createError('Unknown command: ' + name, 'EUNKNOWNCMD', {
command: name
@@ -28,7 +30,7 @@ function help(logger, name, config) {
// -------------------
help.readOptions = function (argv) {
help.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);
var name = options.argv.remain.slice(1).join(' ');

View File

@@ -16,8 +16,7 @@ function home(logger, name, config) {
// If no name is specified, read the project json
// If a name is specified, fetch from the package repository
if (!name) {
promise = project.hasJson()
.then(function (json) {
promise = project.hasJson().then(function(json) {
if (!json) {
throw createError('You are not inside a package', 'ENOENT');
}
@@ -26,14 +25,16 @@ function home(logger, name, config) {
});
} else {
decEndpoint = endpointParser.decompose(name);
promise = project.getPackageRepository().fetch(decEndpoint)
.spread(function (canonicalDir, pkgMeta) {
return pkgMeta;
});
promise = project
.getPackageRepository()
.fetch(decEndpoint)
.spread(function(canonicalDir, pkgMeta) {
return pkgMeta;
});
}
// Get homepage and open it
return promise.then(function (pkgMeta) {
return promise.then(function(pkgMeta) {
var homepage = pkgMeta.homepage;
if (!homepage) {
@@ -47,7 +48,7 @@ function home(logger, name, config) {
// -------------------
home.readOptions = function (argv) {
home.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);
var name = options.argv.remain[1];

View File

@@ -14,7 +14,7 @@ function commandFactory(id) {
var command = require(id);
var commandArgs = [].slice.call(arguments);
return withLogger(function (logger) {
return withLogger(function(logger) {
commandArgs.unshift(logger);
return command.apply(undefined, commandArgs);
@@ -27,7 +27,7 @@ function commandFactory(id) {
commandArgs = command.readOptions(argv);
return withLogger(function (logger) {
return withLogger(function(logger) {
commandArgs.unshift(logger);
return command.apply(undefined, commandArgs);
@@ -37,16 +37,18 @@ function commandFactory(id) {
function withLogger(func) {
var logger = new Logger();
Q.try(func, logger)
.done(function () {
config.restore();
var args = [].slice.call(arguments);
args.unshift('end');
logger.emit.apply(logger, args);
}, function (error) {
config.restore();
logger.emit('error', error);
});
Q.try(func, logger).done(
function() {
config.restore();
var args = [].slice.call(arguments);
args.unshift('end');
logger.emit.apply(logger, args);
},
function(error) {
config.restore();
logger.emit('error', error);
}
);
return logger;
}
@@ -56,11 +58,10 @@ function commandFactory(id) {
return runApi;
}
module.exports = {
cache: {
clean: commandFactory('./cache/clean'),
list: commandFactory('./cache/list'),
list: commandFactory('./cache/list')
},
help: commandFactory('./help'),
home: commandFactory('./home'),

View File

@@ -9,6 +9,13 @@ function info(logger, endpoint, property, config) {
return;
}
// handle @ as version divider
var splitParts = endpoint.split('/');
splitParts[splitParts.length - 1] = splitParts[
splitParts.length - 1
].replace('@', '#');
endpoint = splitParts.join('/');
var repository;
var decEndpoint;
@@ -19,9 +26,10 @@ function info(logger, endpoint, property, config) {
return Q.all([
getPkgMeta(repository, decEndpoint, property),
decEndpoint.target === '*' && !property ? repository.versions(decEndpoint.source) : null
])
.spread(function (pkgMeta, versions) {
decEndpoint.target === '*' && !property
? repository.versions(decEndpoint.source)
: null
]).spread(function(pkgMeta, versions) {
if (versions) {
return {
name: decEndpoint.source,
@@ -35,24 +43,25 @@ function info(logger, endpoint, property, config) {
}
function getPkgMeta(repository, decEndpoint, property) {
return repository.fetch(decEndpoint)
.spread(function (canonicalDir, pkgMeta) {
pkgMeta = mout.object.filter(pkgMeta, function (value, key) {
return key.charAt(0) !== '_';
return repository
.fetch(decEndpoint)
.spread(function(canonicalDir, pkgMeta) {
pkgMeta = mout.object.filter(pkgMeta, function(value, key) {
return key.charAt(0) !== '_';
});
// Retrieve specific property
if (property) {
pkgMeta = mout.object.get(pkgMeta, property);
}
return pkgMeta;
});
// Retrieve specific property
if (property) {
pkgMeta = mout.object.get(pkgMeta, property);
}
return pkgMeta;
});
}
// -------------------
info.readOptions = function (argv) {
info.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);
var pkg = options.argv.remain[1];

View File

@@ -23,31 +23,38 @@ function init(logger, config) {
// This command requires interactive to be enabled
if (!config.interactive) {
throw createError('Register requires an interactive shell', 'ENOINT', {
details: 'Note that you can manually force an interactive shell with --config.interactive'
details:
'Note that you can manually force an interactive shell with --config.interactive'
});
}
project = new Project(config, logger);
// Start with existing JSON details
return readJson(project, logger)
// Fill in defaults
.then(setDefaults.bind(null, config))
// Now prompt user to make changes
.then(promptUser.bind(null, logger))
// Set ignore based on the response
.spread(setIgnore.bind(null, config))
// Set dependencies based on the response
.spread(setDependencies.bind(null, project))
// All done!
.spread(saveJson.bind(null, project, logger));
return (
readJson(project, logger)
// Fill in defaults
.then(setDefaults.bind(null, config))
// Now prompt user to make changes
.then(promptUser.bind(null, logger))
// Set ignore based on the response
.spread(setIgnore.bind(null, config))
// Set dependencies based on the response
.spread(setDependencies.bind(null, project))
// All done!
.spread(saveJson.bind(null, project, logger))
);
}
function readJson(project, logger) {
return project.hasJson()
.then(function (json) {
return project.hasJson().then(function(json) {
if (json) {
logger.warn('existing', 'The existing ' + path.basename(json) + ' file will be used and filled in');
logger.warn(
'existing',
'The existing ' +
path.basename(json) +
' file will be used and filled in'
);
}
return project.getJson();
@@ -56,7 +63,7 @@ function readJson(project, logger) {
function saveJson(project, logger, json) {
// Cleanup empty props (null values, empty strings, objects and arrays)
mout.object.forOwn(json, function (value, key) {
mout.object.forOwn(json, function(value, key) {
if (!validConfigValue(value)) {
delete json[key];
}
@@ -69,8 +76,7 @@ function saveJson(project, logger, json) {
type: 'confirm',
message: 'Looks good?',
default: true
})
.then(function (good) {
}).then(function(good) {
if (!good) {
return null;
}
@@ -84,9 +90,9 @@ function saveJson(project, logger, json) {
// [0]: https://github.com/bower/bower.json-spec
function validConfigValue(val) {
return (
mout.lang.isObject(val) ||
mout.lang.isArray(val) ||
mout.lang.isString(val) ||
mout.lang.isObject(val) ||
mout.lang.isArray(val) ||
mout.lang.isString(val) ||
mout.lang.isBoolean(val) ||
mout.lang.isNumber(val)
);
@@ -116,52 +122,70 @@ function setDefaults(config, json) {
// Homepage
if (!json.homepage) {
// Set as GitHub homepage if it's a GitHub repository
promise = promise.then(function () {
promise = promise.then(function() {
return cmd('git', ['config', '--get', 'remote.origin.url'])
.spread(function (stdout) {
var pair;
.spread(function(stdout) {
var pair;
stdout = stdout.trim();
if (!stdout) {
return;
}
stdout = stdout.trim();
if (!stdout) {
return;
}
pair = GitHubResolver.getOrgRepoPair(stdout);
if (pair) {
json.homepage = 'https://github.com/' + pair.org + '/' + pair.repo;
}
})
.fail(function () { });
pair = GitHubResolver.getOrgRepoPair(stdout);
if (pair) {
json.homepage =
'https://github.com/' + pair.org + '/' + pair.repo;
}
})
.fail(function() {});
});
}
if (!json.authors) {
promise = promise.then(function () {
promise = promise.then(function() {
// Get the user name configured in git
return cmd('git', ['config', '--get', '--global', 'user.name'])
.spread(function (stdout) {
var gitEmail;
var gitName = stdout.trim();
return cmd('git', [
'config',
'--get',
'--global',
'user.name'
]).spread(
function(stdout) {
var gitEmail;
var gitName = stdout.trim();
// Abort if no name specified
if (!gitName) {
return;
}
// Abort if no name specified
if (!gitName) {
return;
}
// Get the user email configured in git
return cmd('git', ['config', '--get', '--global', 'user.email'])
.spread(function (stdout) {
gitEmail = stdout.trim();
}, function () {})
.then(function () {
json.authors = gitName;
json.authors += gitEmail ? ' <' + gitEmail + '>' : '';
});
}, function () {});
// Get the user email configured in git
return cmd('git', [
'config',
'--get',
'--global',
'user.email'
])
.spread(
function(stdout) {
gitEmail = stdout.trim();
},
function() {}
)
.then(function() {
json.authors = gitName;
json.authors += gitEmail
? ' <' + gitEmail + '>'
: '';
});
},
function() {}
);
});
}
return promise.then(function () {
return promise.then(function() {
return json;
});
}
@@ -169,69 +193,73 @@ function setDefaults(config, json) {
function promptUser(logger, json) {
var questions = [
{
'name': 'name',
'message': 'name',
'default': json.name,
'type': 'input'
name: 'name',
message: 'name',
default: json.name,
type: 'input'
},
{
'name': 'description',
'message': 'description',
'default': json.description,
'type': 'input'
name: 'description',
message: 'description',
default: json.description,
type: 'input'
},
{
'name': 'main',
'message': 'main file',
'default': json.main,
'type': 'input'
name: 'main',
message: 'main file',
default: json.main,
type: 'input'
},
{
'name': 'keywords',
'message': 'keywords',
'default': json.keywords ? json.keywords.toString() : null,
'type': 'input'
name: 'keywords',
message: 'keywords',
default: json.keywords ? json.keywords.toString() : null,
type: 'input'
},
{
'name': 'authors',
'message': 'authors',
'default': json.authors ? json.authors.toString() : null,
'type': 'input'
name: 'authors',
message: 'authors',
default: json.authors ? json.authors.toString() : null,
type: 'input'
},
{
'name': 'license',
'message': 'license',
'default': json.license || 'MIT',
'type': 'input'
name: 'license',
message: 'license',
default: json.license || 'MIT',
type: 'input'
},
{
'name': 'homepage',
'message': 'homepage',
'default': json.homepage,
'type': 'input'
name: 'homepage',
message: 'homepage',
default: json.homepage,
type: 'input'
},
{
'name': 'dependencies',
'message': 'set currently installed components as dependencies?',
'default': !mout.object.size(json.dependencies) && !mout.object.size(json.devDependencies),
'type': 'confirm'
name: 'dependencies',
message: 'set currently installed components as dependencies?',
default:
!mout.object.size(json.dependencies) &&
!mout.object.size(json.devDependencies),
type: 'confirm'
},
{
'name': 'ignore',
'message': 'add commonly ignored files to ignore list?',
'default': true,
'type': 'confirm'
name: 'ignore',
message: 'add commonly ignored files to ignore list?',
default: true,
type: 'confirm'
},
{
'name': 'private',
'message': 'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
'default': !!json.private,
'type': 'confirm'
name: 'private',
message:
'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
default: !!json.private,
type: 'confirm'
}
];
return Q.nfcall(logger.prompt.bind(logger), questions)
.then(function (answers) {
return Q.nfcall(logger.prompt.bind(logger), questions).then(function(
answers
) {
json.name = answers.name;
json.description = answers.description;
json.main = answers.main;
@@ -249,12 +277,12 @@ function toArray(value, splitter) {
var arr = value.split(splitter || /[\s,]/);
// Trim values
arr = arr.map(function (item) {
arr = arr.map(function(item) {
return item.trim();
});
// Filter empty values
arr = arr.filter(function (item) {
arr = arr.filter(function(item) {
return !!item;
});
@@ -278,13 +306,12 @@ function setIgnore(config, json, answers) {
function setDependencies(project, json, answers) {
if (answers.dependencies) {
return project.getTree()
.spread(function (tree, flattened, extraneous) {
return project.getTree().spread(function(tree, flattened, extraneous) {
if (extraneous.length) {
json.dependencies = {};
// Add extraneous as dependencies
extraneous.forEach(function (extra) {
extraneous.forEach(function(extra) {
var jsonEndpoint;
// Skip linked packages
@@ -292,7 +319,9 @@ function setDependencies(project, json, answers) {
return;
}
jsonEndpoint = endpointParser.decomposed2json(extra.endpoint);
jsonEndpoint = endpointParser.decomposed2json(
extra.endpoint
);
mout.object.mixIn(json.dependencies, jsonEndpoint);
});
}
@@ -306,7 +335,7 @@ function setDependencies(project, json, answers) {
// -------------------
init.readOptions = function (argv) {
init.readOptions = function(argv) {
return [];
};

View File

@@ -15,7 +15,14 @@ function install(logger, endpoints, options, config) {
// Convert endpoints to decomposed endpoints
endpoints = endpoints || [];
decEndpoints = endpoints.map(function (endpoint) {
decEndpoints = endpoints.map(function(endpoint) {
// handle @ as version divider
var splitParts = endpoint.split('/');
splitParts[splitParts.length - 1] = splitParts[
splitParts.length - 1
].replace('@', '#');
endpoint = splitParts.join('/');
return endpointParser.decompose(endpoint);
});
@@ -24,16 +31,19 @@ function install(logger, endpoints, options, config) {
// -------------------
install.readOptions = function (argv) {
install.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
'force-latest': { type: Boolean, shorthand: 'F'},
'production': { type: Boolean, shorthand: 'p' },
'save': { type: Boolean, shorthand: 'S' },
'save-dev': { type: Boolean, shorthand: 'D' },
'save-exact': { type: Boolean, shorthand: 'E' }
}, argv);
var options = cli.readOptions(
{
'force-latest': { type: Boolean, shorthand: 'F' },
production: { type: Boolean, shorthand: 'p' },
save: { type: Boolean, shorthand: 'S' },
'save-dev': { type: Boolean, shorthand: 'D' },
'save-exact': { type: Boolean, shorthand: 'E' }
},
argv
);
var packages = options.argv.remain.slice(1);

View File

@@ -20,23 +20,24 @@ function linkSelf(logger, config) {
config = defaultConfig(config);
project = new Project(config, logger);
return project.getJson()
.then(function (json) {
return project.getJson().then(function(json) {
var src = config.cwd;
var dst = path.join(config.storage.links, json.name);
// Delete previous link if any
return Q.nfcall(rimraf, dst)
// Link globally
.then(function () {
return createLink(src, dst);
})
.then(function () {
return {
src: src,
dst: dst
};
});
return (
Q.nfcall(rimraf, dst)
// Link globally
.then(function() {
return createLink(src, dst);
})
.then(function() {
return {
src: src,
dst: dst
};
})
);
});
}
@@ -53,27 +54,29 @@ function linkTo(logger, name, localName, config) {
dst = path.join(relativeToBaseDir(config.cwd)(config.directory), localName);
// Delete destination folder if any
return Q.nfcall(rimraf, dst)
// Link locally
.then(function () {
return createLink(src, dst);
})
// Install linked package deps
.then(function () {
return project.update([localName]);
})
.then(function (installed) {
return {
src: src,
dst: dst,
installed: installed
};
});
return (
Q.nfcall(rimraf, dst)
// Link locally
.then(function() {
return createLink(src, dst);
})
// Install linked package deps
.then(function() {
return project.update([localName]);
})
.then(function(installed) {
return {
src: src,
dst: dst,
installed: installed
};
})
);
}
// -------------------
link.readOptions = function (argv) {
link.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);
var name = options.argv.remain[1];

View File

@@ -11,39 +11,48 @@ function list(logger, options, config) {
options = options || {};
// Make relative option true by default when used with paths
if (options.paths && options.relative == null) {
if (options.paths && options.relative == null) {
options.relative = true;
}
config = defaultConfig(config);
project = new Project(config, logger);
return project.getTree(options)
.spread(function (tree, flattened) {
return project.getTree(options).spread(function(tree, flattened) {
// Relativize paths
// Also normalize paths on windows
project.walkTree(tree, function (node) {
if (node.missing) {
return;
}
project.walkTree(
tree,
function(node) {
if (node.missing) {
return;
}
if (options.relative) {
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
}
}, true);
if (options.relative) {
node.canonicalDir = path.relative(
config.cwd,
node.canonicalDir
);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
}
},
true
);
// Note that we need to to parse the flattened tree because it might
// contain additional packages
mout.object.forOwn(flattened, function (node) {
mout.object.forOwn(flattened, function(node) {
if (node.missing) {
return;
}
if (options.relative) {
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
node.canonicalDir = path.relative(
config.cwd,
node.canonicalDir
);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
@@ -61,8 +70,7 @@ function list(logger, options, config) {
}
// Check for new versions
return checkVersions(project, tree, logger)
.then(function () {
return checkVersions(project, tree, logger).then(function() {
return tree;
});
});
@@ -74,32 +82,40 @@ function checkVersions(project, tree, logger) {
var repository = project.getPackageRepository();
// Gather all nodes, ignoring linked nodes
project.walkTree(tree, function (node) {
if (!node.linked) {
nodes.push(node);
}
}, true);
project.walkTree(
tree,
function(node) {
if (!node.linked) {
nodes.push(node);
}
},
true
);
if (nodes.length) {
logger.info('check-new', 'Checking for new versions of the project dependencies...');
logger.info(
'check-new',
'Checking for new versions of the project dependencies...'
);
}
// Check for new versions for each node
promises = nodes.map(function (node) {
promises = nodes.map(function(node) {
var target = node.endpoint.target;
return repository.versions(node.endpoint.source)
.then(function (versions) {
node.versions = versions;
return repository
.versions(node.endpoint.source)
.then(function(versions) {
node.versions = versions;
// Do not check if node's target is not a valid semver one
if (versions.length && semver.validRange(target)) {
node.update = {
target: semver.maxSatisfying(versions, target),
latest: semver.maxSatisfying(versions, '*')
};
}
});
// Do not check if node's target is not a valid semver one
if (versions.length && semver.validRange(target)) {
node.update = {
target: semver.maxSatisfying(versions, target),
latest: semver.maxSatisfying(versions, '*')
};
}
});
});
// Set the versions also for the root node
@@ -111,7 +127,7 @@ function checkVersions(project, tree, logger) {
function paths(flattened) {
var ret = {};
mout.object.forOwn(flattened, function (pkg, name) {
mout.object.forOwn(flattened, function(pkg, name) {
var main;
if (pkg.missing) {
@@ -132,7 +148,7 @@ function paths(flattened) {
}
// Concatenate each main entry with the canonical dir
main = main.map(function (part) {
main = main.map(function(part) {
return normalize(path.join(pkg.canonicalDir, part).trim());
});
@@ -150,13 +166,16 @@ function normalize(src) {
// -------------------
list.readOptions = function (argv) {
list.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
'paths': { type: Boolean, shorthand: 'p' },
'relative': { type: Boolean, shorthand: 'r' }
}, argv);
var options = cli.readOptions(
{
paths: { type: Boolean, shorthand: 'p' },
relative: { type: Boolean, shorthand: 'r' }
},
argv
);
delete options.argv;

View File

@@ -19,24 +19,28 @@ function login(logger, options, config) {
} else {
// This command requires interactive to be enabled
if (!config.interactive) {
logger.emit('error', createError('Login requires an interactive shell', 'ENOINT', {
details: 'Note that you can manually force an interactive shell with --config.interactive'
}));
logger.emit(
'error',
createError('Login requires an interactive shell', 'ENOINT', {
details:
'Note that you can manually force an interactive shell with --config.interactive'
})
);
return;
}
var questions = [
{
'name': 'username',
'message': 'Username',
'type': 'input',
'default': configstore.get('username')
name: 'username',
message: 'Username',
type: 'input',
default: configstore.get('username')
},
{
'name': 'password',
'message': 'Password',
'type': 'password'
name: 'password',
message: 'Password',
type: 'password'
}
];
@@ -44,8 +48,9 @@ function login(logger, options, config) {
version: '3.0.0'
});
promise = Q.nfcall(logger.prompt.bind(logger), questions)
.then(function (answers) {
promise = Q.nfcall(logger.prompt.bind(logger), questions).then(function(
answers
) {
configstore.set('username', answers.username);
github.authenticate({
@@ -56,66 +61,91 @@ function login(logger, options, config) {
return Q.ninvoke(github.authorization, 'create', {
scopes: ['user', 'repo'],
note: 'Bower command line client (' + (new Date()).toISOString() + ')'
note:
'Bower command line client (' +
new Date().toISOString() +
')'
});
});
}
return promise.then(function (result) {
configstore.set('accessToken', result.token);
logger.info('EAUTH', 'Logged in as ' + configstore.get('username'), {});
return promise.then(
function(result) {
configstore.set('accessToken', result.token);
logger.info(
'EAUTH',
'Logged in as ' + configstore.get('username'),
{}
);
return result;
}, function (error) {
var message;
return result;
},
function(error) {
var message;
try {
message = JSON.parse(error.message).message;
} catch (e) {
message = 'Authorization failed';
}
var questions = [
{
'name': 'otpcode',
'message': 'Two-Factor Auth Code',
'type': 'input'
try {
message = JSON.parse(error.message).message;
} catch (e) {
message = 'Authorization failed';
}
];
if (message === 'Must specify two-factor authentication OTP code.') {
return Q.nfcall(logger.prompt.bind(logger), questions)
.then(function (answers) {
return Q.ninvoke(github.authorization, 'create', {
scopes: ['user', 'repo'],
note: 'Bower command line client (' + (new Date()).toISOString() + ')',
headers: {
'X-GitHub-OTP': answers.otpcode
}
});
})
.then(function (result) {
configstore.set('accessToken', result.token);
logger.info('EAUTH', 'Logged in as ' + configstore.get('username'), {});
var questions = [
{
name: 'otpcode',
message: 'Two-Factor Auth Code',
type: 'input'
}
];
return result;
}, function () {
if (
message === 'Must specify two-factor authentication OTP code.'
) {
return Q.nfcall(logger.prompt.bind(logger), questions)
.then(function(answers) {
return Q.ninvoke(github.authorization, 'create', {
scopes: ['user', 'repo'],
note:
'Bower command line client (' +
new Date().toISOString() +
')',
headers: {
'X-GitHub-OTP': answers.otpcode
}
});
})
.then(
function(result) {
configstore.set('accessToken', result.token);
logger.info(
'EAUTH',
'Logged in as ' + configstore.get('username'),
{}
);
return result;
},
function() {
logger.emit('error', createError(message, 'EAUTH'));
}
);
} else {
logger.emit('error', createError(message, 'EAUTH'));
});
} else {
logger.emit('error', createError(message, 'EAUTH'));
}
}
});
);
}
// -------------------
login.readOptions = function (argv) {
login.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
token: { type: String, shorthand: 't' },
}, argv);
var options = cli.readOptions(
{
token: { type: String, shorthand: 't' }
},
argv
);
delete options.argv;

View File

@@ -12,18 +12,21 @@ function lookup(logger, name, config) {
var repository = new PackageRepository(config, logger);
var registryClient = repository.getRegistryClient();
return Q.nfcall(registryClient.lookup.bind(registryClient), name)
.then(function (entry) {
return !entry ? null : {
name: name,
url: entry && entry.url
};
});
return Q.nfcall(registryClient.lookup.bind(registryClient), name).then(
function(entry) {
return !entry
? null
: {
name: name,
url: entry.url
};
}
);
}
// -------------------
lookup.readOptions = function (argv) {
lookup.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);
var name = options.argv.remain[1];

View File

@@ -17,35 +17,40 @@ function clean(project, options, removed) {
// Continually call clean until there is no more extraneous
// dependencies to remove
return project.getTree(options)
.spread(function (tree, flattened, extraneous) {
var names = extraneous.map(function (extra) {
return extra.endpoint.name;
});
return project
.getTree(options)
.spread(function(tree, flattened, extraneous) {
var names = extraneous.map(function(extra) {
return extra.endpoint.name;
});
// Uninstall extraneous
return project.uninstall(names, options)
.then(function (uninstalled) {
// Are we done?
if (!mout.object.size(uninstalled)) {
return removed;
}
// Uninstall extraneous
return project
.uninstall(names, options)
.then(function(uninstalled) {
// Are we done?
if (!mout.object.size(uninstalled)) {
return removed;
}
// Not yet, recurse!
mout.object.mixIn(removed, uninstalled);
return clean(project, options, removed);
// Not yet, recurse!
mout.object.mixIn(removed, uninstalled);
return clean(project, options, removed);
});
});
});
}
// -------------------
prune.readOptions = function (argv) {
prune.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
'production': { type: Boolean, shorthand: 'p' },
}, argv);
var options = cli.readOptions(
{
production: { type: Boolean, shorthand: 'p' }
},
argv
);
delete options.argv;

View File

@@ -4,25 +4,35 @@ var PackageRepository = require('../core/PackageRepository');
var createError = require('../util/createError');
var defaultConfig = require('../config');
function register(logger, name, url, config) {
function register(logger, name, source, config) {
var repository;
var registryClient;
var force;
var url;
var githubSourceRegex = /^\w[\w-]*\/\w[\w-]*$/;
var getGithubUrl = function(source) {
return 'git@github.com:' + source + '.git';
};
config = defaultConfig(config);
force = config.force;
name = (name || '').trim();
url = (url || '').trim();
source = (source || '').trim();
url = source.match(githubSourceRegex) ? getGithubUrl(source) : source;
// Bypass any cache
config.offline = false;
config.force = true;
return Q.try(function () {
return Q.try(function() {
// Verify name and url
if (!name || !url) {
throw createError('Usage: bower register <name> <url>', 'EINVFORMAT');
throw createError(
'Usage: bower register <name> <url>',
'EINVFORMAT'
);
}
// Attempt to resolve the package referenced by the URL to ensure
@@ -30,45 +40,54 @@ function register(logger, name, url, config) {
repository = new PackageRepository(config, logger);
return repository.fetch({ name: name, source: url, target: '*' });
})
.spread(function (canonicalDir, pkgMeta) {
if (pkgMeta.private) {
throw createError('The package you are trying to register is marked as private', 'EPRIV');
}
.spread(function(canonicalDir, pkgMeta) {
if (pkgMeta.private) {
throw createError(
'The package you are trying to register is marked as private',
'EPRIV'
);
}
// If non interactive or user forced, bypass confirmation
if (!config.interactive || force) {
return true;
}
// If non interactive or user forced, bypass confirmation
if (!config.interactive || force) {
return true;
}
// Confirm if the user really wants to register
return Q.nfcall(logger.prompt.bind(logger), {
type: 'confirm',
message: 'Registering a package will make it installable via the registry (' +
chalk.cyan.underline(config.registry.register) + '), continue?',
default: true
// Confirm if the user really wants to register
return Q.nfcall(logger.prompt.bind(logger), {
type: 'confirm',
message:
'Registering a package will make it installable via the registry (' +
chalk.cyan.underline(config.registry.register) +
'), continue?',
default: true
});
})
.then(function(result) {
// If user response was negative, abort
if (!result) {
return;
}
// Register
registryClient = repository.getRegistryClient();
logger.action('register', url, {
name: name,
url: url
});
return Q.nfcall(
registryClient.register.bind(registryClient),
name,
url
);
});
})
.then(function (result) {
// If user response was negative, abort
if (!result) {
return;
}
// Register
registryClient = repository.getRegistryClient();
logger.action('register', url, {
name: name,
url: url
});
return Q.nfcall(registryClient.register.bind(registryClient), name, url);
});
}
// -------------------
register.readOptions = function (argv) {
register.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);

View File

@@ -27,7 +27,7 @@ function search(logger, name, config) {
// -------------------
search.readOptions = function (argv) {
search.readOptions = function(argv) {
var options = cli.readOptions(argv);
var terms = options.argv.remain.slice(1);

View File

@@ -14,54 +14,62 @@ function uninstall(logger, names, options, config) {
config = defaultConfig(config);
project = new Project(config, logger);
return project.getTree(options)
.spread(function (tree, flattened) {
return project.getTree(options).spread(function(tree, flattened) {
// Uninstall nodes
return project.uninstall(names, options)
// Clean out non-shared uninstalled dependencies
.then(function (uninstalled) {
var names = Object.keys(uninstalled);
var children = [];
return (
project
.uninstall(names, options)
// Clean out non-shared uninstalled dependencies
.then(function(uninstalled) {
var names = Object.keys(uninstalled);
var children = [];
// Grab the dependencies of packages that were uninstalled
mout.object.forOwn(flattened, function (node) {
if (names.indexOf(node.endpoint.name) !== -1) {
children.push.apply(children, mout.object.keys(node.dependencies));
}
});
// Grab the dependencies of packages that were uninstalled
mout.object.forOwn(flattened, function(node) {
if (names.indexOf(node.endpoint.name) !== -1) {
children.push.apply(
children,
mout.object.keys(node.dependencies)
);
}
});
// Clean them!
return clean(project, children, uninstalled);
});
// Clean them!
return clean(project, children, uninstalled);
})
);
});
}
function clean(project, names, removed) {
removed = removed || {};
return project.getTree()
.spread(function (tree, flattened) {
return project.getTree().spread(function(tree, flattened) {
var nodes = [];
var dependantsCounter = {};
// Grab the nodes of each specified name
mout.object.forOwn(flattened, function (node) {
mout.object.forOwn(flattened, function(node) {
if (names.indexOf(node.endpoint.name) !== -1) {
nodes.push(node);
}
});
// Walk the down the tree, gathering dependants of the packages
project.walkTree(tree, function (node, nodeName) {
if (names.indexOf(nodeName) !== -1) {
dependantsCounter[nodeName] = dependantsCounter[nodeName] || 0;
dependantsCounter[nodeName] += node.nrDependants;
}
}, true);
project.walkTree(
tree,
function(node, nodeName) {
if (names.indexOf(nodeName) !== -1) {
dependantsCounter[nodeName] =
dependantsCounter[nodeName] || 0;
dependantsCounter[nodeName] += node.nrDependants;
}
},
true
);
// Filter out those that have no dependants
nodes = nodes.filter(function (node) {
nodes = nodes.filter(function(node) {
return !dependantsCounter[node.endpoint.name];
});
@@ -71,39 +79,48 @@ function clean(project, names, removed) {
}
// Grab the nodes after filtering
names = nodes.map(function (node) {
names = nodes.map(function(node) {
return node.endpoint.name;
});
// Uninstall them
return project.uninstall(names)
// Clean out non-shared uninstalled dependencies
.then(function (uninstalled) {
var children;
return (
project
.uninstall(names)
// Clean out non-shared uninstalled dependencies
.then(function(uninstalled) {
var children;
mout.object.mixIn(removed, uninstalled);
mout.object.mixIn(removed, uninstalled);
// Grab the dependencies of packages that were uninstalled
children = [];
nodes.forEach(function (node) {
children.push.apply(children, mout.object.keys(node.dependencies));
});
// Grab the dependencies of packages that were uninstalled
children = [];
nodes.forEach(function(node) {
children.push.apply(
children,
mout.object.keys(node.dependencies)
);
});
// Recurse!
return clean(project, children, removed);
});
// Recurse!
return clean(project, children, removed);
})
);
});
}
// -------------------
uninstall.readOptions = function (argv) {
uninstall.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
'save': { type: Boolean, shorthand: 'S' },
'save-dev': { type: Boolean, shorthand: 'D' }
}, argv);
var options = cli.readOptions(
{
save: { type: Boolean, shorthand: 'S' },
'save-dev': { type: Boolean, shorthand: 'D' }
},
argv
);
var names = options.argv.remain.slice(1);

View File

@@ -6,7 +6,6 @@ var PackageRepository = require('../core/PackageRepository');
var createError = require('../util/createError');
function unregister(logger, name, config) {
if (!name) {
return;
}
@@ -28,46 +27,58 @@ function unregister(logger, name, config) {
repository = new PackageRepository(config, logger);
if (!config.accessToken) {
return logger.emit('error',
createError('Use "bower login" with collaborator credentials', 'EFORBIDDEN')
return logger.emit(
'error',
createError(
'Use "bower login" with collaborator credentials',
'EFORBIDDEN'
)
);
}
return Q.resolve()
.then(function () {
// If non interactive or user forced, bypass confirmation
if (!config.interactive || force) {
return true;
}
.then(function() {
// If non interactive or user forced, bypass confirmation
if (!config.interactive || force) {
return true;
}
return Q.nfcall(logger.prompt.bind(logger), {
type: 'confirm',
message: 'You are about to remove component "' + chalk.cyan.underline(name) + '" from the bower registry (' + chalk.cyan.underline(config.registry.register) + '). It is generally considered bad behavior to remove versions of a library that others are depending on. Are you really sure?',
default: false
return Q.nfcall(logger.prompt.bind(logger), {
type: 'confirm',
message:
'You are about to remove component "' +
chalk.cyan.underline(name) +
'" from the bower registry (' +
chalk.cyan.underline(config.registry.register) +
'). It is generally considered bad behavior to remove versions of a library that others are depending on. Are you really sure?',
default: false
});
})
.then(function(result) {
// If user response was negative, abort
if (!result) {
return;
}
registryClient = repository.getRegistryClient();
logger.action('unregister', name, { name: name });
return Q.nfcall(
registryClient.unregister.bind(registryClient),
name
);
})
.then(function(result) {
logger.info('Package unregistered', name);
return result;
});
})
.then(function (result) {
// If user response was negative, abort
if (!result) {
return;
}
registryClient = repository.getRegistryClient();
logger.action('unregister', name, { name: name });
return Q.nfcall(registryClient.unregister.bind(registryClient), name);
})
.then(function (result) {
logger.info('Package unregistered', name);
return result;
});
}
// -------------------
unregister.readOptions = function (argv) {
unregister.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions(argv);

View File

@@ -18,13 +18,16 @@ function update(logger, names, options, config) {
// -------------------
update.readOptions = function (argv) {
update.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
'force-latest': { type: Boolean, shorthand: 'F' },
'production': { type: Boolean, shorthand: 'p' }
}, argv);
var options = cli.readOptions(
{
'force-latest': { type: Boolean, shorthand: 'F' },
production: { type: Boolean, shorthand: 'p' }
},
argv
);
var names = options.argv.remain.slice(1);

View File

@@ -4,123 +4,205 @@ var fs = require('../util/fs');
var path = require('path');
var Q = require('q');
var execFile = require('child_process').execFile;
var Project = require('../core/Project');
var defaultConfig = require('../config');
var createError = require('../util/createError');
function version(logger, versionArg, options, config) {
var project;
options = options || {};
config = defaultConfig(config);
project = new Project(config, logger);
return bump(project, versionArg, options.message);
return bump(logger, config, versionArg, options.message);
}
function bump(project, versionArg, message) {
var cwd = project._config.cwd || process.cwd();
function bump(logger, config, versionArg, message) {
var cwd = config.cwd || process.cwd();
var newVersion;
var doGitCommit = false;
return checkGit(cwd)
.then(function (hasGit) {
doGitCommit = hasGit;
})
.then(project.getJson.bind(project))
.then(function (json) {
newVersion = getNewVersion(json.version, versionArg);
json.version = newVersion;
})
.then(project.saveJson.bind(project))
.then(function () {
if (doGitCommit) {
return gitCommitAndTag(cwd, newVersion, message);
}
})
.then(function () {
console.log('v' + newVersion);
return newVersion;
});
}
function getNewVersion(currentVersion, versionArg) {
var newVersion = semver.valid(versionArg);
if (!newVersion) {
newVersion = semver.inc(currentVersion, versionArg);
if (!versionArg) {
throw createError('No <version> agrument provided', 'EREADOPTIONS');
}
if (!newVersion) {
throw createError('Invalid version argument: `' + versionArg + '`. Usage: `bower version [<newversion> | major | minor | patch]`', 'EINVALIDVERSION');
}
if (currentVersion === newVersion) {
throw createError('Version not changed', 'EVERSIONNOTCHANGED');
}
return newVersion;
return driver
.check(cwd)
.then(function() {
return Q.all([driver.versions(cwd), driver.currentVersion(cwd)]);
})
.spread(function(versions, currentVersion) {
currentVersion = currentVersion || '0.0.0';
if (semver.valid(versionArg)) {
newVersion = semver.valid(versionArg);
} else {
newVersion = semver.inc(currentVersion, versionArg);
if (!newVersion) {
throw createError(
'Invalid <version> argument: ' + versionArg,
'EINVALIDVERSION',
{ version: versionArg }
);
}
}
newVersion =
currentVersion[0] === 'v' ? 'v' + newVersion : newVersion;
if (versions) {
versions.forEach(function(version) {
if (semver.eq(version, newVersion)) {
throw createError(
'Version exists: ' + newVersion,
'EVERSIONEXISTS',
{ versions: versions, newVersion: newVersion }
);
}
});
}
return driver.bump(cwd, newVersion, message).then(function() {
return {
oldVersion: currentVersion,
newVersion: newVersion
};
});
})
.then(function(result) {
logger.info(
'version',
'Bumped package version from ' +
result.oldVersion +
' to ' +
result.newVersion,
result
);
return result.newVersion;
});
}
function checkGit(cwd) {
var gitDir = path.join(cwd, '.git');
return Q.nfcall(fs.stat, gitDir)
.then(function (stat) {
if (stat.isDirectory()) {
return checkGitStatus(cwd);
var driver = {
check: function(cwd) {
function checkGit(cwd) {
var gitDir = path.join(cwd, '.git');
return Q.nfcall(fs.stat, gitDir).then(
function(stat) {
if (stat.isDirectory()) {
return checkGitStatus(cwd);
}
return false;
},
function() {
//Ignore not found .git directory
return false;
}
);
}
return false;
}, function () {
//Ignore not found .git directory
return false;
});
}
function checkGitStatus(cwd) {
return Q.nfcall(which, 'git')
.fail(function (err) {
err.code = 'ENOGIT';
throw err;
})
.then(function () {
return Q.nfcall(execFile, 'git', ['status', '--porcelain'], {env: process.env, cwd: cwd});
})
.then(function (value) {
var stdout = value[0];
var lines = filterModifiedStatusLines(stdout);
if (lines.length) {
throw createError('Git working directory not clean.\n' + lines.join('\n'), 'EWORKINGDIRECTORYDIRTY');
function checkGitStatus(cwd) {
return Q.nfcall(which, 'git')
.fail(function(err) {
err.code = 'ENOGIT';
throw err;
})
.then(function() {
return Q.nfcall(
execFile,
'git',
['status', '--porcelain'],
{ env: process.env, cwd: cwd }
);
})
.then(function(value) {
var stdout = value[0];
var lines = filterModifiedStatusLines(stdout);
if (lines.length) {
throw createError(
'Version bump requires clean working directory',
'EWORKINGDIRECTORYDIRTY'
);
}
return true;
});
}
return true;
});
}
function filterModifiedStatusLines(stdout) {
return stdout.trim().split('\n')
.filter(function (line) {
return line.trim() && !line.match(/^\?\? /);
}).map(function (line) {
return line.trim();
});
}
function filterModifiedStatusLines(stdout) {
return stdout
.trim()
.split('\n')
.filter(function(line) {
return line.trim() && !line.match(/^\?\? /);
})
.map(function(line) {
return line.trim();
});
}
function gitCommitAndTag(cwd, newVersion, message) {
var tag = 'v' + newVersion;
message = message || tag;
message = message.replace(/%s/g, newVersion);
return Q.nfcall(execFile, 'git', ['add', 'bower.json'], {env: process.env, cwd: cwd})
.then(function () {
return Q.nfcall(execFile, 'git', ['commit', '-m', message], {env: process.env, cwd: cwd});
})
.then(function () {
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {env: process.env, cwd: cwd});
});
}
return checkGit(cwd).then(function(hasGit) {
if (!hasGit) {
throw createError(
'Version bump currently supports only git repositories',
'ENOTGITREPOSITORY'
);
}
});
},
versions: function(cwd) {
return Q.nfcall(execFile, 'git', ['tag'], {
env: process.env,
cwd: cwd
}).then(
function(res) {
var versions = res[0].split(/\r?\n/).filter(semver.valid);
// -------------------
return versions;
},
function() {
return [];
}
);
},
currentVersion: function(cwd) {
return Q.nfcall(execFile, 'git', ['describe', '--abbrev=0', '--tags'], {
env: process.env,
cwd: cwd
}).then(
function(res) {
var version = res[0].split(/\r?\n/).filter(semver.valid)[0];
version.readOptions = function (argv) {
return version;
},
function() {
return undefined;
}
);
},
bump: function(cwd, tag, message) {
message = message || tag;
message = message.replace(/%s/g, tag);
return Q.nfcall(
execFile,
'git',
['commit', '-m', message, '--allow-empty'],
{ env: process.env, cwd: cwd }
).then(function() {
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {
env: process.env,
cwd: cwd
});
});
}
};
version.readOptions = function(argv) {
var cli = require('../util/cli');
var options = cli.readOptions({
'message': { type: String, shorthand: 'm'}
}, argv);
var options = cli.readOptions(
{
message: { type: String, shorthand: 'm' }
},
argv
);
return [options.argv.remain[1], options];
};

View File

@@ -22,26 +22,26 @@ function readCachedConfig(cwd, overwrites) {
// If interactive is auto (null), guess its value
if (config.interactive == null) {
config.interactive = (
process.bin === 'bower' &&
tty.isatty(1) &&
!process.env.CI
);
config.interactive =
process.bin === 'bower' && tty.isatty(1) && !process.env.CI;
}
// Merge common CLI options into the config
if (process.bin === 'bower') {
var cli = require('./util/cli');
object.mixIn(config, cli.readOptions({
force: { type: Boolean, shorthand: 'f' },
offline: { type: Boolean, shorthand: 'o' },
verbose: { type: Boolean, shorthand: 'V' },
quiet: { type: Boolean, shorthand: 'q' },
loglevel: { type: String, shorthand: 'l' },
json: { type: Boolean, shorthand: 'j' },
silent: { type: Boolean, shorthand: 's' }
}));
object.mixIn(
config,
cli.readOptions({
force: { type: Boolean, shorthand: 'f' },
offline: { type: Boolean, shorthand: 'o' },
verbose: { type: Boolean, shorthand: 'V' },
quiet: { type: Boolean, shorthand: 'q' },
loglevel: { type: String, shorthand: 'l' },
json: { type: Boolean, shorthand: 'j' },
silent: { type: Boolean, shorthand: 's' }
})
);
}
return config;

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@ var createError = require('../util/createError');
var RegistryClient = require('bower-registry-client');
function PackageRepository(config, logger) {
var registryOptions;
this._config = config;
@@ -24,7 +23,7 @@ function PackageRepository(config, logger) {
// -----------------
PackageRepository.prototype.fetch = function (decEndpoint) {
PackageRepository.prototype.fetch = function(decEndpoint) {
var logger;
var that = this;
var isTargetable;
@@ -36,132 +35,192 @@ PackageRepository.prototype.fetch = function (decEndpoint) {
// used to fetch
logger = this._logger.geminate();
// Intercept all logs, adding additional information
logger.intercept(function (log) {
logger.intercept(function(log) {
that._extendLog(log, info);
});
return this._getResolver(decEndpoint, logger)
// Decide if we retrieve from the cache or not
// Also decide if we validate the cached entry or not
.then(function (resolver) {
info.resolver = resolver;
isTargetable = resolver.constructor.isTargetable;
return (
this._getResolver(decEndpoint, logger)
// Decide if we retrieve from the cache or not
// Also decide if we validate the cached entry or not
.then(function(resolver) {
info.resolver = resolver;
isTargetable = resolver.constructor.isTargetable;
if (!resolver.isCacheable()) {
return that._resolve(resolver, logger);
}
// If force flag is used, bypass cache, but write to cache anyway
if (that._config.force) {
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
return that._resolve(resolver, logger);
}
// Note that we use the resolver methods to query the
// cache because transformations/normalisations can occur
return that._resolveCache.retrieve(resolver.getSource(), resolver.getTarget())
// Decide if we can use the one from the resolve cache
.spread(function (canonicalDir, pkgMeta) {
// If there's no package in the cache
if (!canonicalDir) {
// And the offline flag is passed, error out
if (that._config.offline) {
throw createError('No cached version for ' + resolver.getSource() + '#' + resolver.getTarget(), 'ENOCACHE', {
resolver: resolver
});
if (!resolver.isCacheable()) {
return that._resolve(resolver, logger);
}
// Otherwise, we have to resolve it
logger.info('not-cached', resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
return that._resolve(resolver, logger);
}
info.canonicalDir = canonicalDir;
info.pkgMeta = pkgMeta;
logger.info('cached', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
// If offline flag is used, use directly the cached one
if (that._config.offline) {
return [canonicalDir, pkgMeta, isTargetable];
}
// Otherwise check for new contents
logger.action('validate', (pkgMeta._release ? pkgMeta._release + ' against ' : '') +
resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
return resolver.hasNew(pkgMeta)
.then(function (hasNew) {
// If there are no new contents, resolve to
// the cached one
if (!hasNew) {
return [canonicalDir, pkgMeta, isTargetable];
// If force flag is used, bypass cache, but write to cache anyway
if (that._config.force) {
logger.action(
'resolve',
resolver.getSource() + '#' + resolver.getTarget()
);
return that._resolve(resolver, logger);
}
// Otherwise resolve to the newest one
logger.info('new', 'version for ' + resolver.getSource() + '#' + resolver.getTarget());
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
// Note that we use the resolver methods to query the
// cache because transformations/normalisations can occur
return (
that._resolveCache
.retrieve(resolver.getSource(), resolver.getTarget())
// Decide if we can use the one from the resolve cache
.spread(function(canonicalDir, pkgMeta) {
// If there's no package in the cache
if (!canonicalDir) {
// And the offline flag is passed, error out
if (that._config.offline) {
throw createError(
'No cached version for ' +
resolver.getSource() +
'#' +
resolver.getTarget(),
'ENOCACHE',
{
resolver: resolver
}
);
}
return that._resolve(resolver, logger);
});
});
})
// If something went wrong, also extend the error
.fail(function (err) {
that._extendLog(err, info);
throw err;
});
// Otherwise, we have to resolve it
logger.info(
'not-cached',
resolver.getSource() +
(resolver.getTarget()
? '#' + resolver.getTarget()
: '')
);
logger.action(
'resolve',
resolver.getSource() +
'#' +
resolver.getTarget()
);
return that._resolve(resolver, logger);
}
info.canonicalDir = canonicalDir;
info.pkgMeta = pkgMeta;
logger.info(
'cached',
resolver.getSource() +
(pkgMeta._release
? '#' + pkgMeta._release
: '')
);
// If offline flag is used, use directly the cached one
if (that._config.offline) {
return [canonicalDir, pkgMeta, isTargetable];
}
// Otherwise check for new contents
logger.action(
'validate',
(pkgMeta._release
? pkgMeta._release + ' against '
: '') +
resolver.getSource() +
(resolver.getTarget()
? '#' + resolver.getTarget()
: '')
);
return resolver
.hasNew(pkgMeta)
.then(function(hasNew) {
// If there are no new contents, resolve to
// the cached one
if (!hasNew) {
return [
canonicalDir,
pkgMeta,
isTargetable
];
}
// Otherwise resolve to the newest one
logger.info(
'new',
'version for ' +
resolver.getSource() +
'#' +
resolver.getTarget()
);
logger.action(
'resolve',
resolver.getSource() +
'#' +
resolver.getTarget()
);
return that._resolve(resolver, logger);
});
})
);
})
// If something went wrong, also extend the error
.fail(function(err) {
that._extendLog(err, info);
throw err;
})
);
};
PackageRepository.prototype.versions = function (source) {
PackageRepository.prototype.versions = function(source) {
// Resolve the source using the factory because the
// source can actually be a registry name
return this._getResolver({ source: source })
.then(function (resolver) {
// If offline, resolve using the cached versions
if (this._config.offline) {
return this._resolveCache.versions(resolver.getSource());
}
return this._getResolver({ source: source }).then(
function(resolver) {
// If offline, resolve using the cached versions
if (this._config.offline) {
return this._resolveCache.versions(resolver.getSource());
}
// Otherwise, fetch remotely
return resolver.constructor.versions(resolver.getSource());
}.bind(this));
// Otherwise, fetch remotely
return resolver.constructor.versions(resolver.getSource());
}.bind(this)
);
};
PackageRepository.prototype.eliminate = function (pkgMeta) {
PackageRepository.prototype.eliminate = function(pkgMeta) {
return Q.all([
this._resolveCache.eliminate(pkgMeta),
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient), pkgMeta.name)
Q.nfcall(
this._registryClient.clearCache.bind(this._registryClient),
pkgMeta.name
)
]);
};
PackageRepository.prototype.clear = function () {
PackageRepository.prototype.clear = function() {
return Q.all([
this._resolveCache.clear(),
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient))
]);
};
PackageRepository.prototype.reset = function () {
PackageRepository.prototype.reset = function() {
this._resolveCache.reset();
this._registryClient.resetCache();
};
PackageRepository.prototype.list = function () {
PackageRepository.prototype.list = function() {
return this._resolveCache.list();
};
PackageRepository.prototype.getRegistryClient = function () {
PackageRepository.prototype.getRegistryClient = function() {
return this._registryClient;
};
PackageRepository.prototype.getResolveCache = function () {
PackageRepository.prototype.getResolveCache = function() {
return this._resolveCache;
};
PackageRepository.clearRuntimeCache = function () {
PackageRepository.clearRuntimeCache = function() {
ResolveCache.clearRuntimeCache();
RegistryClient.clearRuntimeCache();
resolverFactory.clearRuntimeCache();
@@ -169,41 +228,59 @@ PackageRepository.clearRuntimeCache = function () {
// ---------------------
PackageRepository.prototype._getResolver = function (decEndpoint, logger) {
PackageRepository.prototype._getResolver = function(decEndpoint, logger) {
logger = logger || this._logger;
// Get the appropriate resolver
return resolverFactory(decEndpoint, { config: this._config, logger: logger }, this._registryClient);
return resolverFactory(
decEndpoint,
{ config: this._config, logger: logger },
this._registryClient
);
};
PackageRepository.prototype._resolve = function (resolver, logger) {
PackageRepository.prototype._resolve = function(resolver, logger) {
var that = this;
// Resolve the resolver
return resolver.resolve()
// Store in the cache
.then(function (canonicalDir) {
if (!resolver.isCacheable()) {
return canonicalDir;
}
return (
resolver
.resolve()
// Store in the cache
.then(function(canonicalDir) {
if (!resolver.isCacheable()) {
return canonicalDir;
}
return that._resolveCache.store(canonicalDir, resolver.getPkgMeta());
})
// Resolve promise with canonical dir and package meta
.then(function (dir) {
var pkgMeta = resolver.getPkgMeta();
return that._resolveCache.store(
canonicalDir,
resolver.getPkgMeta()
);
})
// Resolve promise with canonical dir and package meta
.then(function(dir) {
var pkgMeta = resolver.getPkgMeta();
logger.info('resolved', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
return [dir, pkgMeta, resolver.constructor.isTargetable()];
});
logger.info(
'resolved',
resolver.getSource() +
(pkgMeta._release ? '#' + pkgMeta._release : '')
);
return [dir, pkgMeta, resolver.constructor.isTargetable()];
})
);
};
PackageRepository.prototype._extendLog = function (log, info) {
PackageRepository.prototype._extendLog = function(log, info) {
log.data = log.data || {};
// Store endpoint info in each log
if (info.decEndpoint) {
log.data.endpoint = mout.object.pick(info.decEndpoint, ['name', 'source', 'target']);
log.data.endpoint = mout.object.pick(info.decEndpoint, [
'name',
'source',
'target'
]);
}
// Store the resolver info in each log

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@ function ResolveCache(config) {
if (!this._cache) {
this._cache = new LRU({
max: 100,
maxAge: 60 * 5 * 1000 // 5 minutes
maxAge: 60 * 5 * 1000 // 5 minutes
});
this.constructor._cache.set(this._dir, this._cache);
}
@@ -40,7 +40,7 @@ function ResolveCache(config) {
// -----------------
ResolveCache.prototype.retrieve = function (source, target) {
ResolveCache.prototype.retrieve = function(source, target) {
var sourceId = md5(source);
var dir = path.join(this._dir, sourceId);
var that = this;
@@ -48,56 +48,57 @@ ResolveCache.prototype.retrieve = function (source, target) {
target = target || '*';
return this._getVersions(sourceId)
.spread(function (versions) {
var suitable;
.spread(function(versions) {
var suitable;
// If target is a semver, find a suitable version
if (semver.validRange(target)) {
suitable = semver.maxSatisfying(versions, target, true);
// If target is a semver, find a suitable version
if (semver.validRange(target)) {
suitable = semver.maxSatisfying(versions, target, true);
if (suitable) {
return suitable;
if (suitable) {
return suitable;
}
}
}
// If target is '*' check if there's a cached '_wildcard'
if (target === '*') {
return mout.array.find(versions, function (version) {
return version === '_wildcard';
// If target is '*' check if there's a cached '_wildcard'
if (target === '*') {
return mout.array.find(versions, function(version) {
return version === '_wildcard';
});
}
// Otherwise check if there's an exact match
return mout.array.find(versions, function(version) {
return version === target;
});
}
})
.then(function(version) {
var canonicalDir;
// Otherwise check if there's an exact match
return mout.array.find(versions, function (version) {
return version === target;
if (!version) {
return [];
}
// Resolve with canonical dir and package meta
canonicalDir = path.join(dir, encodeURIComponent(version));
return that._readPkgMeta(canonicalDir).then(
function(pkgMeta) {
return [canonicalDir, pkgMeta];
},
function() {
// If there was an error, invalidate the in-memory cache,
// delete the cached package and try again
that._cache.del(sourceId);
return Q.nfcall(rimraf, canonicalDir).then(function() {
return that.retrieve(source, target);
});
}
);
});
})
.then(function (version) {
var canonicalDir;
if (!version) {
return [];
}
// Resolve with canonical dir and package meta
canonicalDir = path.join(dir, encodeURIComponent(version));
return that._readPkgMeta(canonicalDir)
.then(function (pkgMeta) {
return [canonicalDir, pkgMeta];
}, function () {
// If there was an error, invalidate the in-memory cache,
// delete the cached package and try again
that._cache.del(sourceId);
return Q.nfcall(rimraf, canonicalDir)
.then(function () {
return that.retrieve(source, target);
});
});
});
};
ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
ResolveCache.prototype.store = function(canonicalDir, pkgMeta) {
var sourceId;
var release;
var dir;
@@ -108,70 +109,82 @@ ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
promise = pkgMeta ? Q.resolve(pkgMeta) : this._readPkgMeta(canonicalDir);
return promise
.then(function (pkgMeta) {
sourceId = md5(pkgMeta._source);
release = that._getPkgRelease(pkgMeta);
dir = path.join(that._dir, sourceId, release);
pkgLock = path.join(that._lockDir, sourceId + '-' + release + '.lock');
.then(function(pkgMeta) {
sourceId = md5(pkgMeta._source);
release = that._getPkgRelease(pkgMeta);
dir = path.join(that._dir, sourceId, release);
pkgLock = path.join(
that._lockDir,
sourceId + '-' + release + '.lock'
);
// Check if destination directory exists to prevent issuing lock at all times
return Q.nfcall(fs.stat, dir)
.fail(function (err) {
var lockParams = { wait: 250, retries: 25, stale: 60000 };
return Q.nfcall(lockFile.lock, pkgLock, lockParams).then(function () {
// Ensure other process didn't start copying files before lock was created
return Q.nfcall(fs.stat, dir)
.fail(function (err) {
// If stat fails, it is expected to return ENOENT
if (err.code !== 'ENOENT') {
throw err;
}
// Check if destination directory exists to prevent issuing lock at all times
return Q.nfcall(fs.stat, dir)
.fail(function(err) {
var lockParams = { wait: 250, retries: 25, stale: 60000 };
return Q.nfcall(lockFile.lock, pkgLock, lockParams)
.then(function() {
// Ensure other process didn't start copying files before lock was created
return Q.nfcall(fs.stat, dir).fail(function(err) {
// If stat fails, it is expected to return ENOENT
if (err.code !== 'ENOENT') {
throw err;
}
// Create missing directory and copy files there
return Q.nfcall(mkdirp, path.dirname(dir)).then(function () {
return Q.nfcall(fs.rename, canonicalDir, dir)
.fail(function (err) {
// If error is EXDEV it means that we are trying to rename
// across different drives, so we copy and remove it instead
if (err.code !== 'EXDEV') {
throw err;
}
// Create missing directory and copy files there
return Q.nfcall(mkdirp, path.dirname(dir)).then(
function() {
return Q.nfcall(
fs.rename,
canonicalDir,
dir
).fail(function(err) {
// If error is EXDEV it means that we are trying to rename
// across different drives, so we copy and remove it instead
if (err.code !== 'EXDEV') {
throw err;
}
return copy.copyDir(canonicalDir, dir);
return copy.copyDir(
canonicalDir,
dir
);
});
}
);
});
})
.finally(function() {
lockFile.unlockSync(pkgLock);
});
});
})
.finally(function() {
// Ensure no tmp dir is left on disk.
return Q.nfcall(rimraf, canonicalDir);
});
}).finally(function () {
lockFile.unlockSync(pkgLock);
});
}).finally(function () {
// Ensure no tmp dir is left on disk.
return Q.nfcall(rimraf, canonicalDir);
})
.then(function() {
var versions = that._cache.get(sourceId);
// Add it to the in memory cache
// and sort the versions afterwards
if (versions && versions.indexOf(release) === -1) {
versions.push(release);
that._sortVersions(versions);
}
// Resolve with the final location
return dir;
});
})
.then(function () {
var versions = that._cache.get(sourceId);
// Add it to the in memory cache
// and sort the versions afterwards
if (versions && versions.indexOf(release) === -1) {
versions.push(release);
that._sortVersions(versions);
}
// Resolve with the final location
return dir;
});
};
ResolveCache.prototype.eliminate = function (pkgMeta) {
ResolveCache.prototype.eliminate = function(pkgMeta) {
var sourceId = md5(pkgMeta._source);
var release = this._getPkgRelease(pkgMeta);
var dir = path.join(this._dir, sourceId, release);
var that = this;
return Q.nfcall(rimraf, dir)
.then(function () {
return Q.nfcall(rimraf, dir).then(function() {
var versions = that._cache.get(sourceId) || [];
mout.array.remove(versions, release);
@@ -182,8 +195,7 @@ ResolveCache.prototype.eliminate = function (pkgMeta) {
if (!versions.length) {
that._cache.del(sourceId);
return that._getVersions(sourceId)
.spread(function (versions) {
return that._getVersions(sourceId).spread(function(versions) {
if (!versions.length) {
// Do not keep in-memory cache if it's completely
// empty
@@ -196,125 +208,144 @@ ResolveCache.prototype.eliminate = function (pkgMeta) {
});
};
ResolveCache.prototype.clear = function () {
ResolveCache.prototype.clear = function() {
return Q.nfcall(rimraf, this._dir)
.then(function () {
return Q.nfcall(fs.mkdir, this._dir);
}.bind(this))
.then(function () {
this._cache.reset();
}.bind(this));
.then(
function() {
return Q.nfcall(fs.mkdir, this._dir);
}.bind(this)
)
.then(
function() {
this._cache.reset();
}.bind(this)
);
};
ResolveCache.prototype.reset = function () {
ResolveCache.prototype.reset = function() {
this._cache.reset();
return this;
};
ResolveCache.prototype.versions = function (source) {
ResolveCache.prototype.versions = function(source) {
var sourceId = md5(source);
return this._getVersions(sourceId)
.spread(function (versions) {
return versions.filter(function (version) {
return this._getVersions(sourceId).spread(function(versions) {
return versions.filter(function(version) {
return semver.valid(version);
});
});
};
ResolveCache.prototype.list = function () {
ResolveCache.prototype.list = function() {
var promises;
var dirs = [];
var that = this;
// Get the list of directories
return Q.nfcall(fs.readdir, this._dir)
.then(function (sourceIds) {
promises = sourceIds.map(function (sourceId) {
return Q.nfcall(fs.readdir, path.join(that._dir, sourceId))
.then(function (versions) {
versions.forEach(function (version) {
var dir = path.join(that._dir, sourceId, version);
dirs.push(dir);
return (
Q.nfcall(fs.readdir, this._dir)
.then(function(sourceIds) {
promises = sourceIds.map(function(sourceId) {
return Q.nfcall(
fs.readdir,
path.join(that._dir, sourceId)
).then(
function(versions) {
versions.forEach(function(version) {
var dir = path.join(
that._dir,
sourceId,
version
);
dirs.push(dir);
});
},
function(err) {
// Ignore lurking files, e.g.: .DS_Store if the user
// has navigated throughout the cache
if (err.code === 'ENOTDIR' && err.path) {
return Q.nfcall(rimraf, err.path);
}
throw err;
}
);
});
}, function (err) {
// Ignore lurking files, e.g.: .DS_Store if the user
// has navigated throughout the cache
if (err.code === 'ENOTDIR' && err.path) {
return Q.nfcall(rimraf, err.path);
}
throw err;
});
});
return Q.all(promises);
})
// Read every package meta
.then(function() {
promises = dirs.map(function(dir) {
return that._readPkgMeta(dir).then(
function(pkgMeta) {
return {
canonicalDir: dir,
pkgMeta: pkgMeta
};
},
function() {
// If it fails to read, invalidate the in memory
// cache for the source and delete the entry directory
var sourceId = path.basename(path.dirname(dir));
that._cache.del(sourceId);
return Q.all(promises);
})
// Read every package meta
.then(function () {
promises = dirs.map(function (dir) {
return that._readPkgMeta(dir)
.then(function (pkgMeta) {
return {
canonicalDir: dir,
pkgMeta: pkgMeta
};
}, function () {
// If it fails to read, invalidate the in memory
// cache for the source and delete the entry directory
var sourceId = path.basename(path.dirname(dir));
that._cache.del(sourceId);
return Q.nfcall(rimraf, dir);
}
);
});
return Q.nfcall(rimraf, dir);
});
});
return Q.all(promises);
})
// Sort by name ASC & release ASC
.then(function(entries) {
// Ignore falsy entries due to errors reading
// package metas
entries = entries.filter(function(entry) {
return !!entry;
});
return Q.all(promises);
})
// Sort by name ASC & release ASC
.then(function (entries) {
// Ignore falsy entries due to errors reading
// package metas
entries = entries.filter(function (entry) {
return !!entry;
});
return entries.sort(function(entry1, entry2) {
var pkgMeta1 = entry1.pkgMeta;
var pkgMeta2 = entry2.pkgMeta;
var comp = pkgMeta1.name.localeCompare(pkgMeta2.name);
return entries.sort(function (entry1, entry2) {
var pkgMeta1 = entry1.pkgMeta;
var pkgMeta2 = entry2.pkgMeta;
var comp = pkgMeta1.name.localeCompare(pkgMeta2.name);
// Sort by name
if (comp) {
return comp;
}
// Sort by name
if (comp) {
return comp;
}
// Sort by version
if (pkgMeta1.version && pkgMeta2.version) {
return semver.compare(
pkgMeta1.version,
pkgMeta2.version
);
}
if (pkgMeta1.version) {
return -1;
}
if (pkgMeta2.version) {
return 1;
}
// Sort by version
if (pkgMeta1.version && pkgMeta2.version) {
return semver.compare(pkgMeta1.version, pkgMeta2.version);
}
if (pkgMeta1.version) {
return -1;
}
if (pkgMeta2.version) {
return 1;
}
// Sort by target
return pkgMeta1._target.localeCompare(pkgMeta2._target);
});
});
// Sort by target
return pkgMeta1._target.localeCompare(pkgMeta2._target);
});
})
);
};
// ------------------------
ResolveCache.clearRuntimeCache = function () {
ResolveCache.clearRuntimeCache = function() {
// Note that _cache refers to the static _cache variable
// that holds other caches per dir!
// Do not confuse it with the instance cache
// Clear cache of each directory
this._cache.forEach(function (cache) {
this._cache.forEach(function(cache) {
cache.reset();
});
@@ -324,8 +355,10 @@ ResolveCache.clearRuntimeCache = function () {
// ------------------------
ResolveCache.prototype._getPkgRelease = function (pkgMeta) {
var release = pkgMeta.version || (pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
ResolveCache.prototype._getPkgRelease = function(pkgMeta) {
var release =
pkgMeta.version ||
(pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
// Encode some dangerous chars such as / and \
release = encodeURIComponent(release);
@@ -333,16 +366,15 @@ ResolveCache.prototype._getPkgRelease = function (pkgMeta) {
return release;
};
ResolveCache.prototype._readPkgMeta = function (dir) {
ResolveCache.prototype._readPkgMeta = function(dir) {
var filename = path.join(dir, '.bower.json');
return readJson(filename)
.spread(function (json) {
return readJson(filename).spread(function(json) {
return json;
});
};
ResolveCache.prototype._getVersions = function (sourceId) {
ResolveCache.prototype._getVersions = function(sourceId) {
var dir;
var versions = this._cache.get(sourceId);
var that = this;
@@ -352,29 +384,31 @@ ResolveCache.prototype._getVersions = function (sourceId) {
}
dir = path.join(this._dir, sourceId);
return Q.nfcall(fs.readdir, dir)
.then(function (versions) {
// Sort and cache in memory
that._sortVersions(versions);
versions = versions.map(decodeURIComponent);
that._cache.set(sourceId, versions);
return [versions, false];
}, function (err) {
// If the directory does not exists, resolve
// as an empty array
if (err.code === 'ENOENT') {
versions = [];
return Q.nfcall(fs.readdir, dir).then(
function(versions) {
// Sort and cache in memory
that._sortVersions(versions);
versions = versions.map(decodeURIComponent);
that._cache.set(sourceId, versions);
return [versions, false];
}
},
function(err) {
// If the directory does not exists, resolve
// as an empty array
if (err.code === 'ENOENT') {
versions = [];
that._cache.set(sourceId, versions);
return [versions, false];
}
throw err;
});
throw err;
}
);
};
ResolveCache.prototype._sortVersions = function (versions) {
ResolveCache.prototype._sortVersions = function(versions) {
// Sort DESC
versions.sort(function (version1, version2) {
versions.sort(function(version1, version2) {
var validSemver1 = semver.valid(version1);
var validSemver2 = semver.valid(version2);
@@ -400,7 +434,7 @@ ResolveCache.prototype._sortVersions = function (versions) {
ResolveCache._cache = new LRU({
max: 5,
maxAge: 60 * 30 * 1000 // 30 minutes
maxAge: 60 * 30 * 1000 // 30 minutes
});
module.exports = ResolveCache;

View File

@@ -13,9 +13,15 @@ function createInstance(decEndpoint, options, registryClient) {
options.version = require('../version');
return getConstructor(decEndpoint, options, registryClient)
.spread(function (ConcreteResolver, decEndpoint) {
return new ConcreteResolver(decEndpoint, options.config, options.logger);
return getConstructor(decEndpoint, options, registryClient).spread(function(
ConcreteResolver,
decEndpoint
) {
return new ConcreteResolver(
decEndpoint,
options.config,
options.logger
);
});
}
@@ -29,8 +35,8 @@ function getConstructor(decEndpoint, options, registryClient) {
var promise = Q.resolve();
var addResolver = function (resolverFactory) {
promise = promise.then(function (result) {
var addResolver = function(resolverFactory) {
promise = promise.then(function(result) {
if (result === undefined) {
return resolverFactory(decEndpoint, options);
} else {
@@ -43,7 +49,7 @@ function getConstructor(decEndpoint, options, registryClient) {
//
// It requires each resolver defined in config.resolvers and calls
// its "match" to check if given resolves supports given decEndpoint
addResolver(function () {
addResolver(function() {
var selectedResolver;
var resolverNames;
@@ -55,28 +61,32 @@ function getConstructor(decEndpoint, options, registryClient) {
resolverNames = [];
}
var resolverPromises = resolverNames.map(function (resolverName) {
var resolverPromises = resolverNames.map(function(resolverName) {
var resolver = resolvers[resolverName];
if (resolver === undefined) {
var resolverPath = resolve(resolverName, { cwd: config.cwd });
if (resolverPath === undefined) {
throw createError('Bower resolver not found: ' + resolverName, 'ENORESOLVER')
throw createError(
'Bower resolver not found: ' + resolverName,
'ENORESOLVER'
);
}
resolver = pluginResolverFactory(require(resolverPath), options);
resolver = pluginResolverFactory(
require(resolverPath),
options
);
}
return function () {
return function() {
if (selectedResolver === undefined) {
var match = resolver.match.bind(resolver);
return Q.fcall(match, source).then(function (result) {
return Q.fcall(match, source).then(function(result) {
if (result) {
return selectedResolver = resolver;
return (selectedResolver = resolver);
}
});
} else {
@@ -85,26 +95,39 @@ function getConstructor(decEndpoint, options, registryClient) {
};
});
return resolverPromises.reduce(Q.when, new Q(undefined)).then(function (resolver) {
if (resolver) {
return Q.fcall(resolver.locate.bind(resolver), decEndpoint.source).then(function (result) {
if (result && result !== decEndpoint.source) {
decEndpoint.source = result;
decEndpoint.registry = true;
return getConstructor(decEndpoint, options, registryClient);
} else {
return [resolver, decEndpoint];
}
});
}
});
return resolverPromises
.reduce(Q.when, new Q(undefined))
.then(function(resolver) {
if (resolver) {
return Q.fcall(
resolver.locate.bind(resolver),
decEndpoint.source
).then(function(result) {
if (result && result !== decEndpoint.source) {
decEndpoint.source = result;
decEndpoint.registry = true;
return getConstructor(
decEndpoint,
options,
registryClient
);
} else {
return [resolver, decEndpoint];
}
});
}
});
});
// Git case: git git+ssh, git+http, git+https
// .git at the end (probably ssh shorthand)
// git@ at the start
addResolver(function () {
if (/^git(\+(ssh|https?))?:\/\//i.test(source) || /\.git\/?$/i.test(source) || /^git@/i.test(source)) {
addResolver(function() {
if (
/^git(\+(ssh|https?))?:\/\//i.test(source) ||
/\.git\/?$/i.test(source) ||
/^git@/i.test(source)
) {
decEndpoint.source = source.replace(/^git\+/, '');
// If it's a GitHub repository, return the specialized resolver
@@ -117,65 +140,74 @@ function getConstructor(decEndpoint, options, registryClient) {
});
// SVN case: svn, svn+ssh, svn+http, svn+https, svn+file
addResolver(function () {
addResolver(function() {
if (/^svn(\+(ssh|https?|file))?:\/\//i.test(source)) {
return [resolvers.Svn, decEndpoint];
}
});
// URL case
addResolver(function () {
addResolver(function() {
if (/^https?:\/\//i.exec(source)) {
return [resolvers.Url, decEndpoint];
}
});
// If source is ./ or ../ or an absolute path
addResolver(function () {
addResolver(function() {
var absolutePath = path.resolve(config.cwd, source);
if (/^\.\.?[\/\\]/.test(source) || /^~\//.test(source) ||
if (
/^\.\.?[\/\\]/.test(source) ||
/^~\//.test(source) ||
path.normalize(source).replace(/[\/\\]+$/, '') === absolutePath
) {
return Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
.then(function (stats) {
decEndpoint.source = absolutePath;
return (
Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
.then(function(stats) {
decEndpoint.source = absolutePath;
if (stats.isDirectory()) {
return Q.resolve([resolvers.GitFs, decEndpoint]);
}
if (stats.isDirectory()) {
return Q.resolve([resolvers.GitFs, decEndpoint]);
}
throw new Error('Not a Git repository');
})
// If not, check if source is a valid Subversion repository
.fail(function () {
return Q.nfcall(fs.stat, path.join(absolutePath, '.svn'))
.then(function (stats) {
decEndpoint.source = absolutePath;
throw new Error('Not a Git repository');
})
// If not, check if source is a valid Subversion repository
.fail(function() {
return Q.nfcall(
fs.stat,
path.join(absolutePath, '.svn')
).then(function(stats) {
decEndpoint.source = absolutePath;
if (stats.isDirectory()) {
return Q.resolve([resolvers.Svn, decEndpoint]);
}
if (stats.isDirectory()) {
return Q.resolve([resolvers.Svn, decEndpoint]);
}
throw new Error('Not a Subversion repository');
});
})
// If not, check if source is a valid file/folder
.fail(function () {
return Q.nfcall(fs.stat, absolutePath)
.then(function () {
decEndpoint.source = absolutePath;
throw new Error('Not a Subversion repository');
});
})
// If not, check if source is a valid file/folder
.fail(function() {
return Q.nfcall(fs.stat, absolutePath).then(function() {
decEndpoint.source = absolutePath;
return Q.resolve([resolvers.Fs, decEndpoint]);
});
});
return Q.resolve([resolvers.Fs, decEndpoint]);
});
})
);
}
});
// Check if is a shorthand and expand it
addResolver(function () {
addResolver(function() {
// Check if the shorthandResolver is falsy
if (!config.shorthandResolver) {
return;
}
// Skip ssh and/or URL with auth
if (/[:@]/.test(source)) {
return;
@@ -184,26 +216,34 @@ function getConstructor(decEndpoint, options, registryClient) {
// Ensure exactly only one "/"
var parts = source.split('/');
if (parts.length === 2) {
decEndpoint.source = mout.string.interpolate(config.shorthandResolver, {
shorthand: source,
owner: parts[0],
package: parts[1]
});
decEndpoint.source = mout.string.interpolate(
config.shorthandResolver,
{
shorthand: source,
owner: parts[0],
package: parts[1]
}
);
return getConstructor(decEndpoint, options, registryClient);
}
});
// As last resort, we try the registry
addResolver(function () {
addResolver(function() {
if (!registryClient) {
return;
}
return Q.nfcall(registryClient.lookup.bind(registryClient), source)
.then(function (entry) {
return Q.nfcall(
registryClient.lookup.bind(registryClient),
source
).then(function(entry) {
if (!entry) {
throw createError('Package ' + source + ' not found', 'ENOTFOUND');
throw createError(
'Package ' + source + ' not found',
'ENOTFOUND'
);
}
decEndpoint.registry = true;
@@ -218,15 +258,18 @@ function getConstructor(decEndpoint, options, registryClient) {
});
});
addResolver(function () {
throw createError('Could not find appropriate resolver for ' + source, 'ENORESOLVER');
addResolver(function() {
throw createError(
'Could not find appropriate resolver for ' + source,
'ENORESOLVER'
);
});
return promise;
}
function clearRuntimeCache() {
mout.object.values(resolvers).forEach(function (ConcreteResolver) {
mout.object.values(resolvers).forEach(function(ConcreteResolver) {
ConcreteResolver.clearRuntimeCache();
});
}

View File

@@ -17,13 +17,19 @@ function FsResolver(decEndpoint, config, logger) {
// If target was specified, simply reject the promise
if (this._target !== '*') {
throw createError('File system sources can\'t resolve targets', 'ENORESTARGET');
throw createError(
"File system sources can't resolve targets",
'ENORESTARGET'
);
}
// If the name was guessed
if (this._guessedName) {
// Remove extension
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
this._name = this._name.substr(
0,
this._name.length - path.extname(this._name).length
);
}
}
@@ -32,7 +38,7 @@ mout.object.mixIn(FsResolver, Resolver);
// -----------------
FsResolver.isTargetable = function () {
FsResolver.isTargetable = function() {
return false;
};
@@ -42,19 +48,18 @@ FsResolver.isTargetable = function () {
// TODO: There's room for improvement by using streams if the source
// is an archive file, by piping read stream to the zip extractor
// This will likely increase the complexity of code but might worth it
FsResolver.prototype._resolve = function () {
FsResolver.prototype._resolve = function() {
return this._copy()
.then(this._extract.bind(this))
.then(this._rename.bind(this));
.then(this._extract.bind(this))
.then(this._rename.bind(this));
};
// -----------------
FsResolver.prototype._copy = function () {
FsResolver.prototype._copy = function() {
var that = this;
return Q.nfcall(fs.stat, this._source)
.then(function (stat) {
return Q.nfcall(fs.stat, this._source).then(function(stat) {
var dst;
var copyOpts;
var promise;
@@ -68,22 +73,24 @@ FsResolver.prototype._copy = function () {
// Read the bower.json inside the folder, so that we
// copy only the necessary files if it has ignore specified
promise = that._readJson(that._source)
.then(function (json) {
copyOpts.ignore = json.ignore;
return copy.copyDir(that._source, dst, copyOpts);
})
.then(function () {
// Resolve to null because it's a dir
return;
});
// Else it's a file
promise = that
._readJson(that._source)
.then(function(json) {
copyOpts.ignore = json.ignore;
return copy.copyDir(that._source, dst, copyOpts);
})
.then(function() {
// Resolve to null because it's a dir
return;
});
// Else it's a file
} else {
dst = path.join(that._tempDir, path.basename(that._source));
promise = copy.copyFile(that._source, dst, copyOpts)
.then(function () {
return dst;
});
promise = copy
.copyFile(that._source, dst, copyOpts)
.then(function() {
return dst;
});
}
that._logger.action('copy', that._source, {
@@ -95,7 +102,7 @@ FsResolver.prototype._copy = function () {
});
};
FsResolver.prototype._extract = function (file) {
FsResolver.prototype._extract = function(file) {
if (!file || !extract.canExtract(file)) {
return Q.resolve();
}
@@ -108,30 +115,34 @@ FsResolver.prototype._extract = function (file) {
return extract(file, this._tempDir);
};
FsResolver.prototype._rename = function () {
return Q.nfcall(fs.readdir, this._tempDir)
.then(function (files) {
var file;
var oldPath;
var newPath;
FsResolver.prototype._rename = function() {
return Q.nfcall(fs.readdir, this._tempDir).then(
function(files) {
var file;
var oldPath;
var newPath;
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
// Only rename if there's only one file and it's not the json
if (files.length === 1 && !/^(bower|component)\.json$/.test(files[0])) {
file = files[0];
this._singleFile = 'index' + path.extname(file);
oldPath = path.join(this._tempDir, file);
newPath = path.join(this._tempDir, this._singleFile);
// Only rename if there's only one file and it's not the json
if (
files.length === 1 &&
!/^(bower|component)\.json$/.test(files[0])
) {
file = files[0];
this._singleFile = 'index' + path.extname(file);
oldPath = path.join(this._tempDir, file);
newPath = path.join(this._tempDir, this._singleFile);
return Q.nfcall(fs.rename, oldPath, newPath);
}
}.bind(this));
return Q.nfcall(fs.rename, oldPath, newPath);
}
}.bind(this)
);
};
FsResolver.prototype._savePkgMeta = function (meta) {
FsResolver.prototype._savePkgMeta = function(meta) {
// Store main if is a single file
if (this._singleFile) {
meta.main = this._singleFile;

View File

@@ -19,31 +19,52 @@ mout.object.mixIn(GitFsResolver, GitResolver);
// -----------------
// Override the checkout function to work with the local copy
GitFsResolver.prototype._checkout = function () {
GitFsResolver.prototype._checkout = function() {
var resolution = this._resolution;
// The checkout process could be similar to the GitRemoteResolver by prepending file:// to the source
// But from my performance measures, it's faster to copy the folder and just checkout in there
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
this._logger.action(
'checkout',
resolution.tag || resolution.branch || resolution.commit,
{
resolution: resolution,
to: this._tempDir
}
);
// Copy files to the temporary directory first
return this._copy()
.then(cmd.bind(cmd, 'git', ['checkout', '-f', resolution.tag || resolution.branch || resolution.commit], { cwd: this._tempDir }))
// Cleanup unstaged files
.then(cmd.bind(cmd, 'git', ['clean', '-f', '-d'], { cwd: this._tempDir }));
return (
this._copy()
.then(
cmd.bind(
cmd,
'git',
[
'checkout',
'-f',
resolution.tag || resolution.branch || resolution.commit
],
{ cwd: this._tempDir }
)
)
// Cleanup unstaged files
.then(
cmd.bind(cmd, 'git', ['clean', '-f', '-d'], {
cwd: this._tempDir
})
)
);
};
GitFsResolver.prototype._copy = function () {
GitFsResolver.prototype._copy = function() {
return copy.copyDir(this._source, this._tempDir);
};
// -----------------
// Grab refs locally
GitFsResolver.refs = function (source) {
GitFsResolver.refs = function(source) {
var value;
// TODO: Normalize source because of the various available protocols?
@@ -52,20 +73,24 @@ GitFsResolver.refs = function (source) {
return Q.resolve(value);
}
value = cmd('git', ['show-ref', '--tags', '--heads'], { cwd : source })
.spread(function (stdout) {
var refs;
value = cmd('git', ['show-ref', '--tags', '--heads'], {
cwd: source
}).spread(
function(stdout) {
var refs;
refs = stdout.toString()
.trim() // Trim trailing and leading spaces
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
.split(/[\r\n]+/); // Split lines into an array
refs = stdout
.toString()
.trim() // Trim trailing and leading spaces
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
.split(/[\r\n]+/); // Split lines into an array
// Update the refs with the actual refs
this._cache.refs.set(source, refs);
// Update the refs with the actual refs
this._cache.refs.set(source, refs);
return refs;
}.bind(this));
return refs;
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value

View File

@@ -29,16 +29,13 @@ function GitHubResolver(decEndpoint, config, logger) {
this._source += '.git';
}
// Check if it's public
this._public = mout.string.startsWith(this._source, 'git://');
// Use https:// rather than git:// if on a proxy
if (this._config.proxy || this._config.httpsProxy) {
this._source = this._source.replace('git://', 'https://');
}
// Enable shallow clones for GitHub repos
this._shallowClone = function () {
this._shallowClone = function() {
return Q.resolve(true);
};
}
@@ -48,15 +45,21 @@ mout.object.mixIn(GitHubResolver, GitRemoteResolver);
// -----------------
GitHubResolver.prototype._checkout = function () {
// Only fully works with public repositories and tags
// Could work with https/ssh protocol but not with 100% certainty
if (!this._public || !this._resolution.tag) {
return GitRemoteResolver.prototype._checkout.call(this);
}
GitHubResolver.prototype._checkout = function() {
var msg;
var tarballUrl = 'https://github.com/' + this._org + '/' + this._repo + '/archive/' + this._resolution.tag + '.tar.gz';
var name =
this._resolution.tag ||
this._resolution.branch ||
this._resolution.commit;
var tarballUrl =
'https://github.com/' +
this._org +
'/' +
this._repo +
'/archive/' +
name +
'.tar.gz';
var file = path.join(this._tempDir, 'archive.tar.gz');
var reqHeaders = {};
var that = this;
@@ -77,55 +80,88 @@ GitHubResolver.prototype._checkout = function () {
timeout: this._config.timeout,
headers: reqHeaders
})
.progress(function (state) {
// Retry?
if (state.retry) {
msg = 'Download of ' + tarballUrl + ' failed with ' + state.error.code + ', ';
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
that._logger.debug('error', state.error.message, { error: state.error });
return that._logger.warn('retry', msg);
}
.progress(function(state) {
// Retry?
if (state.retry) {
msg =
'Download of ' +
tarballUrl +
' failed with ' +
state.error.code +
', ';
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
that._logger.debug('error', state.error.message, {
error: state.error
});
return that._logger.warn('retry', msg);
}
// Progress
msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
if (state.total) {
msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
msg += state.percent + '%';
}
that._logger.info('progress', msg);
})
.then(function () {
// Extract archive
that._logger.action('extract', path.basename(file), {
archive: file,
to: that._tempDir
});
// Progress
msg =
'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
if (state.total) {
msg +=
' of ' +
(state.total / 1024 / 1024).toFixed(1) +
'MB downloaded, ';
msg += state.percent + '%';
}
that._logger.info('progress', msg);
})
.then(
function() {
// Extract archive
that._logger.action('extract', path.basename(file), {
archive: file,
to: that._tempDir
});
return extract(file, that._tempDir)
// Fallback to standard git clone if extraction failed
.fail(function (err) {
msg = 'Decompression of ' + path.basename(file) + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
msg += 'trying with git..';
that._logger.debug('error', err.message, { error: err });
that._logger.warn('retry', msg);
return (
extract(file, that._tempDir)
// Fallback to standard git clone if extraction failed
.fail(function(err) {
msg =
'Decompression of ' +
path.basename(file) +
' failed' +
(err.code ? ' with ' + err.code : '') +
', ';
msg += 'trying with git..';
that._logger.debug('error', err.message, {
error: err
});
that._logger.warn('retry', msg);
return that._cleanTempDir()
.then(GitRemoteResolver.prototype._checkout.bind(that));
});
// Fallback to standard git clone if download failed
}, function (err) {
msg = 'Download of ' + tarballUrl + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
msg += 'trying with git..';
that._logger.debug('error', err.message, { error: err });
that._logger.warn('retry', msg);
return that
._cleanTempDir()
.then(
GitRemoteResolver.prototype._checkout.bind(
that
)
);
})
);
// Fallback to standard git clone if download failed
},
function(err) {
msg =
'Download of ' +
tarballUrl +
' failed' +
(err.code ? ' with ' + err.code : '') +
', ';
msg += 'trying with git..';
that._logger.debug('error', err.message, { error: err });
that._logger.warn('retry', msg);
return that._cleanTempDir()
.then(GitRemoteResolver.prototype._checkout.bind(that));
});
return that
._cleanTempDir()
.then(GitRemoteResolver.prototype._checkout.bind(that));
}
);
};
GitHubResolver.prototype._savePkgMeta = function (meta) {
GitHubResolver.prototype._savePkgMeta = function(meta) {
// Set homepage if not defined
if (!meta.homepage) {
meta.homepage = 'https://github.com/' + this._org + '/' + this._repo;
@@ -136,10 +172,12 @@ GitHubResolver.prototype._savePkgMeta = function (meta) {
// ----------------
GitHubResolver.getOrgRepoPair = function (url) {
GitHubResolver.getOrgRepoPair = function(url) {
var match;
match = url.match(/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i);
match = url.match(
/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i
);
if (!match) {
return null;
}

View File

@@ -19,14 +19,14 @@ function GitRemoteResolver(decEndpoint, config, logger) {
this._name = this._name.slice(0, -4);
}
// Get the host of this source
// Get the remote of this source
if (!/:\/\//.test(this._source)) {
this._host = url.parse('ssh://' + this._source).host;
this._remote = url.parse('ssh://' + this._source);
} else {
this._host = url.parse(this._source).host;
this._remote = url.parse(this._source);
}
this._remote = url.parse(this._source);
this._host = this._remote.host;
// Verify whether the server supports shallow cloning
this._shallowClone = this._supportsShallowCloning;
@@ -37,33 +37,37 @@ mout.object.mixIn(GitRemoteResolver, GitResolver);
// -----------------
GitRemoteResolver.prototype._checkout = function () {
GitRemoteResolver.prototype._checkout = function() {
var promise;
var timer;
var reporter;
var that = this;
var resolution = this._resolution;
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
this._logger.action(
'checkout',
resolution.tag || resolution.branch || resolution.commit,
{
resolution: resolution,
to: this._tempDir
}
);
// If resolution is a commit, we need to clone the entire repo and check it out
// Because a commit is not a named ref, there's no better solution
if (resolution.type === 'commit') {
promise = this._slowClone(resolution);
// Otherwise we are checking out a named ref so we can optimize it
// Otherwise we are checking out a named ref so we can optimize it
} else {
promise = this._fastClone(resolution);
}
// Throttle the progress reporter to 1 time each sec
reporter = mout.fn.throttle(function (data) {
reporter = mout.fn.throttle(function(data) {
var lines;
lines = data.split(/[\r\n]+/);
lines.forEach(function (line) {
lines.forEach(function(line) {
if (/\d{1,3}\%/.test(line)) {
// TODO: There are some strange chars that appear once in a while (\u001b[K)
// Trim also those?
@@ -73,94 +77,125 @@ GitRemoteResolver.prototype._checkout = function () {
}, 1000);
// Start reporting progress after a few seconds
timer = setTimeout(function () {
timer = setTimeout(function() {
promise.progress(reporter);
}, 8000);
return promise
// Add additional proxy information to the error if necessary
.fail(function (err) {
that._suggestProxyWorkaround(err);
throw err;
})
// Clear timer at the end
.fin(function () {
clearTimeout(timer);
reporter.cancel();
});
return (
promise
// Add additional proxy information to the error if necessary
.fail(function(err) {
that._suggestProxyWorkaround(err);
throw err;
})
// Clear timer at the end
.fin(function() {
clearTimeout(timer);
reporter.cancel();
})
);
};
GitRemoteResolver.prototype._findResolution = function (target) {
GitRemoteResolver.prototype._findResolution = function(target) {
var that = this;
// Override this function to include a meaningful message related to proxies
// if necessary
return GitResolver.prototype._findResolution.call(this, target)
.fail(function (err) {
that._suggestProxyWorkaround(err);
throw err;
});
return GitResolver.prototype._findResolution
.call(this, target)
.fail(function(err) {
that._suggestProxyWorkaround(err);
throw err;
});
};
// ------------------------------
GitRemoteResolver.prototype._slowClone = function (resolution) {
return cmd('git', ['clone', this._source, this._tempDir, '--progress'])
.then(cmd.bind(cmd, 'git', ['checkout', resolution.commit], { cwd: this._tempDir }));
GitRemoteResolver.prototype._slowClone = function(resolution) {
return cmd('git', [
'clone',
this._source,
this._tempDir,
'--progress'
]).then(
cmd.bind(cmd, 'git', ['checkout', resolution.commit], {
cwd: this._tempDir
})
);
};
GitRemoteResolver.prototype._fastClone = function (resolution) {
GitRemoteResolver.prototype._fastClone = function(resolution) {
var branch,
args,
that = this;
branch = resolution.tag || resolution.branch;
args = ['clone', this._source, '-b', branch, '--progress', '.'];
args = ['clone', this._source, '-b', branch, '--progress', '.'];
return this._shallowClone().then(function (shallowCloningSupported) {
return this._shallowClone().then(function(shallowCloningSupported) {
// If the host does not support shallow clones, we don't use --depth=1
if (shallowCloningSupported && !GitRemoteResolver._noShallow.get(that._host)) {
if (
shallowCloningSupported &&
!GitRemoteResolver._noShallow.get(that._host)
) {
args.push('--depth', 1);
}
return cmd('git', args, { cwd: that._tempDir })
.spread(function (stdout, stderr) {
// Only after 1.7.10 --branch accepts tags
// Detect those cases and inform the user to update git otherwise it's
// a lot slower than newer versions
if (!/branch .+? not found/i.test(stderr)) {
return;
}
return cmd('git', args, { cwd: that._tempDir }).spread(
function(stdout, stderr) {
// Only after 1.7.10 --branch accepts tags
// Detect those cases and inform the user to update git otherwise it's
// a lot slower than newer versions
if (!/branch .+? not found/i.test(stderr)) {
return;
}
that._logger.warn('old-git', 'It seems you are using an old version of git, it will be slower and propitious to errors!');
return cmd('git', ['checkout', resolution.commit], { cwd: that._tempDir });
}, function (err) {
// Some git servers do not support shallow clones
// When that happens, we mark this host and try again
if (!GitRemoteResolver._noShallow.has(that._source) &&
err.details &&
/(rpc failed|shallow|--depth)/i.test(err.details)
that._logger.warn(
'old-git',
'It seems you are using an old version of git, it will be slower and propitious to errors!'
);
return cmd('git', ['checkout', resolution.commit], {
cwd: that._tempDir
});
},
function(err) {
// Some git servers do not support shallow clones
// When that happens, we mark this host and try again
if (
!GitRemoteResolver._noShallow.has(that._source) &&
err.details &&
/(rpc failed|shallow|--depth)/i.test(err.details)
) {
GitRemoteResolver._noShallow.set(that._host, true);
return that._fastClone(resolution);
}
GitRemoteResolver._noShallow.set(that._host, true);
return that._fastClone(resolution);
}
throw err;
});
throw err;
}
);
});
};
GitRemoteResolver.prototype._suggestProxyWorkaround = function (err) {
if ((this._config.proxy || this._config.httpsProxy) &&
GitRemoteResolver.prototype._suggestProxyWorkaround = function(err) {
if (
(this._config.proxy || this._config.httpsProxy) &&
mout.string.startsWith(this._source, 'git://') &&
err.code === 'ECMDERR' && err.details
err.code === 'ECMDERR' &&
err.details
) {
err.details = err.details.trim();
err.details += '\n\nWhen under a proxy, you must configure git to use https:// instead of git://.';
err.details += '\nYou can configure it for every endpoint or for this specific host as follows:';
err.details +=
'\n\nWhen under a proxy, you must configure git to use https:// instead of git://.';
err.details +=
'\nYou can configure it for every endpoint or for this specific host as follows:';
err.details += '\ngit config --global url."https://".insteadOf git://';
err.details += '\ngit config --global url."https://' + this._host + '".insteadOf git://' + this._host;
err.details += 'Ignore this suggestion if you already have this configured.';
err.details +=
'\ngit config --global url."https://' +
this._host +
'".insteadOf git://' +
this._host;
err.details +=
'Ignore this suggestion if you already have this configured.';
}
};
@@ -182,7 +217,7 @@ GitRemoteResolver.prototype._suggestProxyWorkaround = function (err) {
// negotiation that needs to take place.
//
// The above should cover most cases, including BitBucket.
GitRemoteResolver.prototype._supportsShallowCloning = function () {
GitRemoteResolver.prototype._supportsShallowCloning = function() {
var value = true;
// Verify that the remote could be parsed and that a protocol is set
@@ -191,8 +226,11 @@ GitRemoteResolver.prototype._supportsShallowCloning = function () {
return Q.resolve(false);
}
if (!this._host || !this._config.shallowCloneHosts || this._config.shallowCloneHosts.indexOf(this._host) === -1) {
if (
!this._host ||
!this._config.shallowCloneHosts ||
this._config.shallowCloneHosts.indexOf(this._host) === -1
) {
return Q.resolve(false);
}
@@ -200,34 +238,43 @@ GitRemoteResolver.prototype._supportsShallowCloning = function () {
// HTTP or HTTPS, not for Git or SSH.
// Also check for hosts that have been checked in a previous request and have been found to support
// shallow cloning.
if (mout.string.startsWith(this._remote.protocol, 'http')
&& !GitRemoteResolver._canShallow.get(this._host)) {
if (
mout.string.startsWith(this._remote.protocol, 'http') &&
!GitRemoteResolver._canShallow.get(this._host)
) {
// Provide GIT_CURL_VERBOSE=2 environment variable to capture curl output.
// Calling ls-remote includes a call to the git-upload-pack service, which returns the content type in the response.
var processEnv = mout.object.merge(process.env, { 'GIT_CURL_VERBOSE': '2' });
var processEnv = mout.object.merge(process.env, {
GIT_CURL_VERBOSE: '2'
});
value = cmd('git', ['ls-remote', '--heads', this._source], {
env: processEnv
})
.spread(function (stdout, stderr) {
// Check stderr for content-type, ignore stdout
var isSmartServer;
}).spread(
function(stdout, stderr) {
// Check stderr for content-type, ignore stdout
var isSmartServer;
// If the content type is 'x-git', then the server supports shallow cloning
isSmartServer = mout.string.contains(stderr,
'Content-Type: application/x-git-upload-pack-advertisement');
// If the content type is 'x-git', then the server supports shallow cloning
isSmartServer = mout.string.contains(
stderr,
'Content-Type: application/x-git-upload-pack-advertisement'
);
this._logger.debug('detect-smart-git', 'Smart Git host detected: ' + isSmartServer);
this._logger.debug(
'detect-smart-git',
'Smart Git host detected: ' + isSmartServer
);
if (isSmartServer) {
// Cache this host
GitRemoteResolver._canShallow.set(this._host, true);
}
if (isSmartServer) {
// Cache this host
GitRemoteResolver._canShallow.set(this._host, true);
}
return isSmartServer;
}.bind(this));
}
else {
return isSmartServer;
}.bind(this)
);
} else {
// One of the following cases:
// * A non-HTTP/HTTPS protocol
// * A host that has been checked before and that supports shallow cloning
@@ -240,7 +287,7 @@ GitRemoteResolver.prototype._supportsShallowCloning = function () {
// ------------------------------
// Grab refs remotely
GitRemoteResolver.refs = function (source) {
GitRemoteResolver.refs = function(source) {
var value;
// TODO: Normalize source because of the various available protocols?
@@ -250,20 +297,22 @@ GitRemoteResolver.refs = function (source) {
}
// Store the promise in the refs object
value = cmd('git', ['ls-remote', '--tags', '--heads', source])
.spread(function (stdout) {
var refs;
value = cmd('git', ['ls-remote', '--tags', '--heads', source]).spread(
function(stdout) {
var refs;
refs = stdout.toString()
.trim() // Trim trailing and leading spaces
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
.split(/[\r\n]+/); // Split lines into an array
refs = stdout
.toString()
.trim() // Trim trailing and leading spaces
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
.split(/[\r\n]+/); // Split lines into an array
// Update the refs with the actual refs
this._cache.refs.set(source, refs);
// Update the refs with the actual refs
this._cache.refs.set(source, refs);
return refs;
}.bind(this));
return refs;
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value

View File

@@ -26,8 +26,18 @@ function GitResolver(decEndpoint, config, logger) {
// anyway
mkdirp.sync(config.storage.empty);
process.env.GIT_TEMPLATE_DIR = config.storage.empty;
process.env.GIT_SSL_NO_VERIFY = (!config.strictSsl).toString();
process.env.GIT_TERMINAL_PROMPT = config.interactive ? '1' : '0';
if (!config.strictSsl) {
process.env.GIT_SSL_NO_VERIFY = 'true';
}
if (!config.interactive) {
process.env.GIT_TERMINAL_PROMPT = '0';
if (!process.env.SSH_ASKPASS) {
process.env.SSH_ASKPASS = 'echo';
}
}
Resolver.call(this, decEndpoint, config, logger);
@@ -41,18 +51,20 @@ mout.object.mixIn(GitResolver, Resolver);
// -----------------
GitResolver.prototype._hasNew = function (pkgMeta) {
GitResolver.prototype._hasNew = function(pkgMeta) {
var oldResolution = pkgMeta._resolution || {};
return this._findResolution()
.then(function (resolution) {
return this._findResolution().then(function(resolution) {
// Check if resolution types are different
if (oldResolution.type !== resolution.type) {
return true;
}
// If resolved to a version, there is new content if the tags are not equal
if (resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag)) {
if (
resolution.type === 'version' &&
semver.neq(resolution.tag, oldResolution.tag)
) {
return true;
}
@@ -61,35 +73,37 @@ GitResolver.prototype._hasNew = function (pkgMeta) {
});
};
GitResolver.prototype._resolve = function () {
GitResolver.prototype._resolve = function() {
var that = this;
return this._findResolution()
.then(function () {
return that._checkout()
// Always run cleanup after checkout to ensure that .git is removed!
// If it's not removed, problems might arise when the "tmp" module attempts
// to delete the temporary folder
.fin(function () {
return that._cleanup();
});
return this._findResolution().then(function() {
return (
that
._checkout()
// Always run cleanup after checkout to ensure that .git is removed!
// If it's not removed, problems might arise when the "tmp" module attempts
// to delete the temporary folder
.fin(function() {
return that._cleanup();
})
);
});
};
// -----------------
// Abstract functions that should be implemented by concrete git resolvers
GitResolver.prototype._checkout = function () {
GitResolver.prototype._checkout = function() {
throw new Error('_checkout not implemented');
};
GitResolver.refs = function (source) {
GitResolver.refs = function(source) {
throw new Error('refs not implemented');
};
// -----------------
GitResolver.prototype._findResolution = function (target) {
GitResolver.prototype._findResolution = function(target) {
var err;
var self = this.constructor;
var that = this;
@@ -98,20 +112,15 @@ GitResolver.prototype._findResolution = function (target) {
// Target is a commit, so it's a stale target (not a moving target)
// There's nothing to do in this case
if ((/^[a-f0-9]{40}$/).test(target)) {
if (/^[a-f0-9]{40}$/.test(target)) {
this._resolution = { type: 'commit', commit: target };
return Q.resolve(this._resolution);
}
// Target is a range/version
if (semver.validRange(target)) {
return self.versions(this._source, true)
.then(function (versions) {
var versionsArr,
version,
index;
versionsArr = versions.map(function (obj) { return obj.version; });
return self.versions(this._source, true).then(function(versions) {
var versionsArr, version, index;
// If there are no tags and target is *,
// fallback to the latest commit on master
@@ -119,98 +128,143 @@ GitResolver.prototype._findResolution = function (target) {
return that._findResolution('master');
}
versionsArr = versions.map(function (obj) { return obj.version; });
versionsArr = versions.map(function(obj) {
return obj.version;
});
// Find a satisfying version, enabling strict match so that pre-releases
// have lower priority over normal ones when target is *
index = semver.maxSatisfyingIndex(versionsArr, target, true);
if (index !== -1) {
version = versions[index];
return that._resolution = { type: 'version', tag: version.tag, commit: version.commit };
return (that._resolution = {
type: 'version',
tag: version.tag,
commit: version.commit
});
}
// Check if there's an exact branch/tag with this name as last resort
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
]).spread(function(branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
return (that._resolution = {
type: 'tag',
tag: target,
commit: tags[target]
});
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
return (that._resolution = {
type: 'branch',
branch: target,
commit: branches[target]
});
}
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
details: !versions.length ?
'No versions found in ' + that._source :
'Available versions in ' + that._source + ': ' + versions.map(function (version) { return version.version; }).join(', ')
});
throw createError(
'No tag found that was able to satisfy ' + target,
'ENORESTARGET',
{
details: !versions.length
? 'No versions found in ' + that._source
: 'Available versions in ' +
that._source +
': ' +
versions
.map(function(version) {
return version.version;
})
.join(', ')
}
);
});
});
}
// Otherwise, target is either a tag or a branch
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
if ((/^[a-f0-9]{4,40}$/).test(target)) {
if (target.length < 12) {
that._logger.warn(
'short-sha',
'Consider using longer commit SHA to avoid conflicts'
);
return Q.all([self.branches(that._source), self.tags(that._source)]).spread(
function(branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return (that._resolution = {
type: 'tag',
tag: target,
commit: tags[target]
});
}
if (mout.object.hasOwn(branches, target)) {
return (that._resolution = {
type: 'branch',
branch: target,
commit: branches[target]
});
}
that._resolution = { type: 'commit', commit: target };
return that._resolution;
if (/^[a-f0-9]{4,40}$/.test(target)) {
if (target.length < 12) {
that._logger.warn(
'short-sha',
'Consider using longer commit SHA to avoid conflicts'
);
}
that._resolution = { type: 'commit', commit: target };
return that._resolution;
}
branches = Object.keys(branches);
tags = Object.keys(tags);
err = createError(
'Tag/branch ' + target + ' does not exist',
'ENORESTARGET'
);
err.details = !tags.length
? 'No tags found in ' + that._source
: 'Available tags: ' + tags.join(', ');
err.details += '\n';
err.details += !branches.length
? 'No branches found in ' + that._source
: 'Available branches: ' + branches.join(', ');
throw err;
}
branches = Object.keys(branches);
tags = Object.keys(tags);
err = createError('Tag/branch ' + target + ' does not exist', 'ENORESTARGET');
err.details = !tags.length ?
'No tags found in ' + that._source :
'Available tags: ' + tags.join(', ');
err.details += '\n';
err.details += !branches.length ?
'No branches found in ' + that._source :
'Available branches: ' + branches.join(', ');
throw err;
});
);
};
GitResolver.prototype._cleanup = function () {
GitResolver.prototype._cleanup = function() {
var gitFolder = path.join(this._tempDir, '.git');
return Q.nfcall(rimraf, gitFolder);
};
GitResolver.prototype._savePkgMeta = function (meta) {
GitResolver.prototype._savePkgMeta = function(meta) {
var version;
if (this._resolution.type === 'version') {
version = semver.clean(this._resolution.tag);
// Warn if the package meta version is different than the resolved one
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
resolution: this._resolution,
pkgMeta: meta
});
if (
typeof meta.version === 'string' &&
semver.valid(meta.version) &&
semver.neq(meta.version, version)
) {
this._logger.warn(
'mismatch',
'Version declared in the json (' +
meta.version +
') is different than the resolved one (' +
version +
')',
{
resolution: this._resolution,
pkgMeta: meta
}
);
}
// Ensure package meta version is the same as the resolution
@@ -224,9 +278,10 @@ GitResolver.prototype._savePkgMeta = function (meta) {
// Save version/tag/commit in the release
// Note that we can't store branches because _release is supposed to be
// an unique id of this ref.
meta._release = version ||
this._resolution.tag ||
this._resolution.commit.substr(0, 10);
meta._release =
version ||
this._resolution.tag ||
this._resolution.commit.substr(0, 10);
// Save resolution to be used in hasNew later
meta._resolution = this._resolution;
@@ -236,51 +291,56 @@ GitResolver.prototype._savePkgMeta = function (meta) {
// ------------------------------
GitResolver.versions = function (source, extra) {
GitResolver.versions = function(source, extra) {
var value = this._cache.versions.get(source);
if (value) {
return Q.resolve(value)
.then(function () {
var versions = this._cache.versions.get(source);
return Q.resolve(value).then(
function() {
var versions = this._cache.versions.get(source);
// If no extra information was requested,
// resolve simply with the versions
if (!extra) {
versions = versions.map(function (version) {
return version.version;
});
}
// If no extra information was requested,
// resolve simply with the versions
if (!extra) {
versions = versions.map(function(version) {
return version.version;
});
}
return versions;
}.bind(this));
return versions;
}.bind(this)
);
}
value = this.tags(source)
.then(function (tags) {
var tag;
var version;
var versions = [];
value = this.tags(source).then(
function(tags) {
var tag;
var version;
var versions = [];
// For each tag
for (tag in tags) {
version = semver.clean(tag);
if (version) {
versions.push({ version: version, tag: tag, commit: tags[tag] });
// For each tag
for (tag in tags) {
version = semver.clean(tag);
if (version) {
versions.push({
version: version,
tag: tag,
commit: tags[tag]
});
}
}
}
// Sort them by DESC order
versions.sort(function (a, b) {
return semver.rcompare(a.version, b.version);
});
// Sort them by DESC order
versions.sort(function(a, b) {
return semver.rcompare(a.version, b.version);
});
this._cache.versions.set(source, versions);
// Call the function again to keep it DRY
return this.versions(source, extra);
}.bind(this));
this._cache.versions.set(source, versions);
// Call the function again to keep it DRY
return this.versions(source, extra);
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value
@@ -289,30 +349,31 @@ GitResolver.versions = function (source, extra) {
return value;
};
GitResolver.tags = function (source) {
GitResolver.tags = function(source) {
var value = this._cache.tags.get(source);
if (value) {
return Q.resolve(value);
}
value = this.refs(source)
.then(function (refs) {
var tags = {};
value = this.refs(source).then(
function(refs) {
var tags = {};
// For each line in the refs, match only the tags
refs.forEach(function (line) {
var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/);
// For each line in the refs, match only the tags
refs.forEach(function(line) {
var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/);
if (match && !mout.string.endsWith(match[2], '^{}')) {
tags[match[2]] = match[1];
}
});
if (match && !mout.string.endsWith(match[2], '^{}')) {
tags[match[2]] = match[1];
}
});
this._cache.tags.set(source, tags);
this._cache.tags.set(source, tags);
return tags;
}.bind(this));
return tags;
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value
@@ -321,32 +382,33 @@ GitResolver.tags = function (source) {
return value;
};
GitResolver.branches = function (source) {
GitResolver.branches = function(source) {
var value = this._cache.branches.get(source);
if (value) {
return Q.resolve(value);
}
value = this.refs(source)
.then(function (refs) {
var branches = {};
value = this.refs(source).then(
function(refs) {
var branches = {};
// For each line in the refs, extract only the heads
// Organize them in an object where keys are branches and values
// the commit hashes
refs.forEach(function (line) {
var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/);
// For each line in the refs, extract only the heads
// Organize them in an object where keys are branches and values
// the commit hashes
refs.forEach(function(line) {
var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/);
if (match) {
branches[match[2]] = match[1];
}
});
if (match) {
branches[match[2]] = match[1];
}
});
this._cache.branches.set(source, branches);
this._cache.branches.set(source, branches);
return branches;
}.bind(this));
return branches;
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value
@@ -355,9 +417,9 @@ GitResolver.branches = function (source) {
return value;
};
GitResolver.clearRuntimeCache = function () {
GitResolver.clearRuntimeCache = function() {
// Reset cache for branches, tags, etc
mout.object.forOwn(GitResolver._cache, function (lru) {
mout.object.forOwn(GitResolver._cache, function(lru) {
lru.reset();
});
};

View File

@@ -24,27 +24,27 @@ function Resolver(decEndpoint, config, logger) {
// -----------------
Resolver.prototype.getSource = function () {
Resolver.prototype.getSource = function() {
return this._source;
};
Resolver.prototype.getName = function () {
Resolver.prototype.getName = function() {
return this._name;
};
Resolver.prototype.getTarget = function () {
Resolver.prototype.getTarget = function() {
return this._target;
};
Resolver.prototype.getTempDir = function () {
Resolver.prototype.getTempDir = function() {
return this._tempDir;
};
Resolver.prototype.getPkgMeta = function () {
Resolver.prototype.getPkgMeta = function() {
return this._pkgMeta;
};
Resolver.prototype.hasNew = function (pkgMeta) {
Resolver.prototype.hasNew = function(pkgMeta) {
var promise;
var that = this;
@@ -58,12 +58,12 @@ Resolver.prototype.hasNew = function (pkgMeta) {
// Avoid reading the package meta if already given
promise = this._hasNew(pkgMeta);
return promise.fin(function () {
return promise.fin(function() {
that._working = false;
});
};
Resolver.prototype.resolve = function () {
Resolver.prototype.resolve = function() {
var that = this;
// If already working, error out
@@ -74,89 +74,104 @@ Resolver.prototype.resolve = function () {
this._working = true;
// Create temporary dir
return this._createTempDir()
// Resolve self
.then(this._resolve.bind(this))
// Read json, generating the package meta
.then(this._readJson.bind(this, null))
// Apply and save package meta
.then(function (meta) {
return that._applyPkgMeta(meta)
.then(that._savePkgMeta.bind(that, meta));
})
.then(function () {
// Resolve with the folder
return that._tempDir;
}, function (err) {
// If something went wrong, unset the temporary dir
that._tempDir = null;
throw err;
})
.fin(function () {
that._working = false;
});
return (
this._createTempDir()
// Resolve self
.then(this._resolve.bind(this))
// Read json, generating the package meta
.then(this._readJson.bind(this, null))
// Apply and save package meta
.then(function(meta) {
return that
._applyPkgMeta(meta)
.then(that._savePkgMeta.bind(that, meta));
})
.then(
function() {
// Resolve with the folder
return that._tempDir;
},
function(err) {
// If something went wrong, unset the temporary dir
that._tempDir = null;
throw err;
}
)
.fin(function() {
that._working = false;
})
);
};
Resolver.prototype.isCacheable = function () {
Resolver.prototype.isCacheable = function() {
// Bypass cache for local dependencies
if (this._source &&
if (
this._source &&
/^(?:file:[\/\\]{2}|[A-Z]:)?\.?\.?[\/\\]/.test(this._source)
) {
return false;
}
// We don't want to cache moving targets like branches
if (this._pkgMeta &&
if (
this._pkgMeta &&
this._pkgMeta._resolution &&
this._pkgMeta._resolution.type === 'branch') {
this._pkgMeta._resolution.type === 'branch'
) {
return false;
}
return true;
};
// -----------------
// Abstract functions that must be implemented by concrete resolvers
Resolver.prototype._resolve = function () {
Resolver.prototype._resolve = function() {
throw new Error('_resolve not implemented');
};
// Abstract functions that can be re-implemented by concrete resolvers
// as necessary
Resolver.prototype._hasNew = function (pkgMeta) {
Resolver.prototype._hasNew = function(pkgMeta) {
return Q.resolve(true);
};
Resolver.isTargetable = function () {
Resolver.isTargetable = function() {
return true;
};
Resolver.versions = function (source) {
Resolver.versions = function(source) {
return Q.resolve([]);
};
Resolver.clearRuntimeCache = function () {};
Resolver.clearRuntimeCache = function() {};
// -----------------
Resolver.prototype._createTempDir = function () {
Resolver.prototype._createTempDir = function() {
return Q.nfcall(mkdirp, this._config.tmp)
.then(function () {
return Q.nfcall(tmp.dir, {
template: path.join(this._config.tmp, md5(this._name) + '-' + process.pid + '-XXXXXX'),
mode: 0777 & ~process.umask(),
unsafeCleanup: true
});
}.bind(this))
.then(function (dir) {
// nfcall may return multiple callback arguments as an array
return this._tempDir = Array.isArray(dir) ? dir[0] : dir;
}.bind(this));
.then(
function() {
return Q.nfcall(tmp.dir, {
template: path.join(
this._config.tmp,
md5(this._name) + '-' + process.pid + '-XXXXXX'
),
mode: 0777 & ~process.umask(),
unsafeCleanup: true
});
}.bind(this)
)
.then(
function(dir) {
// nfcall may return multiple callback arguments as an array
return (this._tempDir = Array.isArray(dir) ? dir[0] : dir);
}.bind(this)
);
};
Resolver.prototype._cleanTempDir = function () {
Resolver.prototype._cleanTempDir = function() {
var tempDir = this._tempDir;
if (!tempDir) {
@@ -165,31 +180,37 @@ Resolver.prototype._cleanTempDir = function () {
// Delete and create folder
return Q.nfcall(rimraf, tempDir)
.then(function () {
return Q.nfcall(mkdirp, tempDir, 0777 & ~process.umask());
})
.then(function () {
return tempDir;
});
.then(function() {
return Q.nfcall(mkdirp, tempDir, 0777 & ~process.umask());
})
.then(function() {
return tempDir;
});
};
Resolver.prototype._readJson = function (dir) {
Resolver.prototype._readJson = function(dir) {
var that = this;
dir = dir || this._tempDir;
return readJson(dir, {
assume: { name: this._name }
})
.spread(function (json, deprecated) {
assume: { name: this._name },
logger: that._logger
}).spread(function(json, deprecated) {
if (deprecated) {
that._logger.warn('deprecated', 'Package ' + that._name + ' is using the deprecated ' + deprecated);
that._logger.warn(
'deprecated',
'Package ' +
that._name +
' is using the deprecated ' +
deprecated
);
}
return json;
});
};
Resolver.prototype._applyPkgMeta = function (meta) {
Resolver.prototype._applyPkgMeta = function(meta) {
// Check if name defined in the json is different
// If so and if the name was "guessed", assume the json name
if (meta.name !== this._name && this._guessedName) {
@@ -203,13 +224,12 @@ Resolver.prototype._applyPkgMeta = function (meta) {
}
// Otherwise remove them from the temp dir
return removeIgnores(this._tempDir, meta)
.then(function () {
return removeIgnores(this._tempDir, meta).then(function() {
return meta;
});
};
Resolver.prototype._savePkgMeta = function (meta) {
Resolver.prototype._savePkgMeta = function(meta) {
var that = this;
var contents;
@@ -217,21 +237,15 @@ Resolver.prototype._savePkgMeta = function (meta) {
meta._source = this._source;
meta._target = this._target;
['main', 'ignore'].forEach(function (attr) {
if (meta[attr]) return;
that._logger.log(
'warn', 'invalid-meta',
(meta.name || 'component') + ' is missing "' + attr + '" entry in bower.json'
);
});
// Stringify contents
contents = JSON.stringify(meta, null, 2);
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
.then(function () {
return that._pkgMeta = meta;
return Q.nfcall(
fs.writeFile,
path.join(this._tempDir, '.bower.json'),
contents
).then(function() {
return (that._pkgMeta = meta);
});
};

View File

@@ -31,27 +31,29 @@ mout.object.mixIn(SvnResolver, Resolver);
// -----------------
SvnResolver.getSource = function (source) {
SvnResolver.getSource = function(source) {
var uri = this._source || source;
return uri
.replace(/^svn\+(https?|file):\/\//i, '$1://') // Change svn+http or svn+https or svn+file to http(s), file respectively
.replace('svn://', 'http://') // Change svn to http
.replace(/\/+$/, ''); // Remove trailing slashes
.replace(/^svn\+(https?|file):\/\//i, '$1://') // Change svn+http or svn+https or svn+file to http(s), file respectively
.replace('svn://', 'http://') // Change svn to http
.replace(/\/+$/, ''); // Remove trailing slashes
};
SvnResolver.prototype._hasNew = function (pkgMeta) {
SvnResolver.prototype._hasNew = function(pkgMeta) {
var oldResolution = pkgMeta._resolution || {};
return this._findResolution()
.then(function (resolution) {
return this._findResolution().then(function(resolution) {
// Check if resolution types are different
if (oldResolution.type !== resolution.type) {
return true;
}
// If resolved to a version, there is new content if the tags are not equal
if (resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag)) {
if (
resolution.type === 'version' &&
semver.neq(resolution.tag, oldResolution.tag)
) {
return true;
}
@@ -60,18 +62,17 @@ SvnResolver.prototype._hasNew = function (pkgMeta) {
});
};
SvnResolver.prototype._resolve = function () {
SvnResolver.prototype._resolve = function() {
var that = this;
return this._findResolution()
.then(function () {
return this._findResolution().then(function() {
return that._export();
});
};
// -----------------
SvnResolver.prototype._export = function () {
SvnResolver.prototype._export = function() {
var promise;
var timer;
var reporter;
@@ -80,27 +81,56 @@ SvnResolver.prototype._export = function () {
this.source = SvnResolver.getSource(this._source);
this._logger.action('export', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
this._logger.action(
'export',
resolution.tag || resolution.branch || resolution.commit,
{
resolution: resolution,
to: this._tempDir
}
);
if (resolution.type === 'commit') {
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
promise = cmd('svn', [
'export',
'--force',
'--non-interactive',
this._source + '/trunk',
'-r' + resolution.commit,
this._tempDir
]);
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/trunk', this._tempDir]);
promise = cmd('svn', [
'export',
'--force',
'--non-interactive',
this._source + '/trunk',
this._tempDir
]);
} else if (resolution.type === 'branch') {
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/branches/' + resolution.branch, this._tempDir]);
promise = cmd('svn', [
'export',
'--force',
'--non-interactive',
this._source + '/branches/' + resolution.branch,
this._tempDir
]);
} else {
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/tags/' + resolution.tag, this._tempDir]);
promise = cmd('svn', [
'export',
'--force',
'--non-interactive',
this._source + '/tags/' + resolution.tag,
this._tempDir
]);
}
// Throttle the progress reporter to 1 time each sec
reporter = mout.fn.throttle(function (data) {
reporter = mout.fn.throttle(function(data) {
var lines;
lines = data.split(/[\r\n]+/);
lines.forEach(function (line) {
lines.forEach(function(line) {
if (/\d{1,3}\%/.test(line)) {
// TODO: There are some strange chars that appear once in a while (\u001b[K)
// Trim also those?
@@ -110,25 +140,27 @@ SvnResolver.prototype._export = function () {
}, 1000);
// Start reporting progress after a few seconds
timer = setTimeout(function () {
timer = setTimeout(function() {
promise.progress(reporter);
}, 8000);
return promise
// Add additional proxy information to the error if necessary
.fail(function (err) {
throw err;
})
// Clear timer at the end
.fin(function () {
clearTimeout(timer);
reporter.cancel();
});
return (
promise
// Add additional proxy information to the error if necessary
.fail(function(err) {
throw err;
})
// Clear timer at the end
.fin(function() {
clearTimeout(timer);
reporter.cancel();
})
);
};
// -----------------
SvnResolver.prototype._findResolution = function (target) {
SvnResolver.prototype._findResolution = function(target) {
var err;
var self = this.constructor;
var that = this;
@@ -139,7 +171,7 @@ SvnResolver.prototype._findResolution = function (target) {
// Target is a revision, so it's a stale target (not a moving target)
// There's nothing to do in this case
if ((/^r\d+/).test(target)) {
if (/^r\d+/.test(target)) {
target = target.split('r');
this._resolution = { type: 'commit', commit: target[1] };
@@ -148,13 +180,12 @@ SvnResolver.prototype._findResolution = function (target) {
// Target is a range/version
if (semver.validRange(target)) {
return self.versions(this._source, true)
.then(function (versions) {
var versionsArr,
version,
index;
return self.versions(this._source, true).then(function(versions) {
var versionsArr, version, index;
versionsArr = versions.map(function (obj) { return obj.version; });
versionsArr = versions.map(function(obj) {
return obj.version;
});
// If there are no tags and target is *,
// fallback to the latest commit on trunk
@@ -162,80 +193,124 @@ SvnResolver.prototype._findResolution = function (target) {
return that._findResolution('trunk');
}
versionsArr = versions.map(function (obj) { return obj.version; });
versionsArr = versions.map(function(obj) {
return obj.version;
});
// Find a satisfying version, enabling strict match so that pre-releases
// have lower priority over normal ones when target is *
index = semver.maxSatisfyingIndex(versionsArr, target, true);
if (index !== -1) {
version = versions[index];
return that._resolution = { type: 'version', tag: version.tag, commit: version.commit };
return (that._resolution = {
type: 'version',
tag: version.tag,
commit: version.commit
});
}
// Check if there's an exact branch/tag with this name as last resort
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
]).spread(function(branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
return (that._resolution = {
type: 'tag',
tag: target,
commit: tags[target]
});
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
return (that._resolution = {
type: 'branch',
branch: target,
commit: branches[target]
});
}
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
details: !versions.length ?
'No versions found in ' + that._source :
'Available versions in ' + that._source + ': ' + versions.map(function (version) { return version.version; }).join(', ')
});
throw createError(
'No tag found that was able to satisfy ' + target,
'ENORESTARGET',
{
details: !versions.length
? 'No versions found in ' + that._source
: 'Available versions in ' +
that._source +
': ' +
versions
.map(function(version) {
return version.version;
})
.join(', ')
}
);
});
});
}
// Otherwise, target is either a tag or a branch
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
return Q.all([self.branches(that._source), self.tags(that._source)]).spread(
function(branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return (that._resolution = {
type: 'tag',
tag: target,
commit: tags[target]
});
}
if (mout.object.hasOwn(branches, target)) {
return (that._resolution = {
type: 'branch',
branch: target,
commit: branches[target]
});
}
branches = Object.keys(branches);
tags = Object.keys(tags);
err = createError(
'target ' + target + ' does not exist',
'ENORESTARGET'
);
err.details = !tags.length
? 'No tags found in ' + that._source
: 'Available tags: ' + tags.join(', ');
err.details += '\n';
err.details += !branches.length
? 'No branches found in ' + that._source
: 'Available branches: ' + branches.join(', ');
throw err;
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
branches = Object.keys(branches);
tags = Object.keys(tags);
err = createError('target ' + target + ' does not exist', 'ENORESTARGET');
err.details = !tags.length ?
'No tags found in ' + that._source :
'Available tags: ' + tags.join(', ');
err.details += '\n';
err.details += !branches.length ?
'No branches found in ' + that._source :
'Available branches: ' + branches.join(', ');
throw err;
});
);
};
SvnResolver.prototype._savePkgMeta = function (meta) {
SvnResolver.prototype._savePkgMeta = function(meta) {
var version;
if (this._resolution.type === 'version') {
version = semver.clean(this._resolution.tag);
// Warn if the package meta version is different than the resolved one
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
resolution: this._resolution,
pkgMeta: meta
});
if (
typeof meta.version === 'string' &&
semver.neq(meta.version, version)
) {
this._logger.warn(
'mismatch',
'Version declared in the json (' +
meta.version +
') is different than the resolved one (' +
version +
')',
{
resolution: this._resolution,
pkgMeta: meta
}
);
}
// Ensure package meta version is the same as the resolution
@@ -249,9 +324,7 @@ SvnResolver.prototype._savePkgMeta = function (meta) {
// Save version/tag/commit in the release
// Note that we can't store branches because _release is supposed to be
// an unique id of this ref.
meta._release = version ||
this._resolution.tag ||
this._resolution.commit;
meta._release = version || this._resolution.tag || this._resolution.commit;
// Save resolution to be used in hasNew later
meta._resolution = this._resolution;
@@ -261,52 +334,58 @@ SvnResolver.prototype._savePkgMeta = function (meta) {
// ------------------------------
SvnResolver.versions = function (source, extra) {
SvnResolver.versions = function(source, extra) {
source = SvnResolver.getSource(source);
var value = this._cache.versions.get(source);
if (value) {
return Q.resolve(value)
.then(function () {
var versions = this._cache.versions.get(source);
return Q.resolve(value).then(
function() {
var versions = this._cache.versions.get(source);
// If no extra information was requested,
// resolve simply with the versions
if (!extra) {
versions = versions.map(function (version) {
return version.version;
});
}
// If no extra information was requested,
// resolve simply with the versions
if (!extra) {
versions = versions.map(function(version) {
return version.version;
});
}
return versions;
}.bind(this));
return versions;
}.bind(this)
);
}
value = this.tags(source)
.then(function (tags) {
var tag;
var version;
var versions = [];
value = this.tags(source).then(
function(tags) {
var tag;
var version;
var versions = [];
// For each tag
for (tag in tags) {
version = semver.clean(tag);
if (version) {
versions.push({ version: version, tag: tag, commit: tags[tag] });
// For each tag
for (tag in tags) {
version = semver.clean(tag);
if (version) {
versions.push({
version: version,
tag: tag,
commit: tags[tag]
});
}
}
}
// Sort them by DESC order
versions.sort(function (a, b) {
return semver.rcompare(a.version, b.version);
});
// Sort them by DESC order
versions.sort(function(a, b) {
return semver.rcompare(a.version, b.version);
});
this._cache.versions.set(source, versions);
this._cache.versions.set(source, versions);
// Call the function again to keep it DRY
return this.versions(source, extra);
}.bind(this));
// Call the function again to keep it DRY
return this.versions(source, extra);
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value
@@ -315,7 +394,7 @@ SvnResolver.versions = function (source, extra) {
return value;
};
SvnResolver.tags = function (source) {
SvnResolver.tags = function(source) {
source = SvnResolver.getSource(source);
var value = this._cache.tags.get(source);
@@ -324,14 +403,19 @@ SvnResolver.tags = function (source) {
return Q.resolve(value);
}
value = cmd('svn', ['list', source + '/tags', '--verbose', '--non-interactive'])
.spread(function (stout) {
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
value = cmd('svn', [
'list',
source + '/tags',
'--verbose',
'--non-interactive'
]).spread(
function(stout) {
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
this._cache.tags.set(source, tags);
return tags;
}.bind(this));
this._cache.tags.set(source, tags);
return tags;
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value
@@ -340,7 +424,7 @@ SvnResolver.tags = function (source) {
return value;
};
SvnResolver.branches = function (source) {
SvnResolver.branches = function(source) {
source = SvnResolver.getSource(source);
var value = this._cache.branches.get(source);
@@ -349,17 +433,24 @@ SvnResolver.branches = function (source) {
return Q.resolve(value);
}
value = cmd('svn', ['list', source + '/branches', '--verbose', '--non-interactive'])
.spread(function (stout) {
var branches = SvnResolver.parseSubversionListOutput(stout.toString());
value = cmd('svn', [
'list',
source + '/branches',
'--verbose',
'--non-interactive'
]).spread(
function(stout) {
var branches = SvnResolver.parseSubversionListOutput(
stout.toString()
);
// trunk is a branch!
branches.trunk = '*';
// trunk is a branch!
branches.trunk = '*';
this._cache.branches.set(source, branches);
return branches;
}.bind(this));
this._cache.branches.set(source, branches);
return branches;
}.bind(this)
);
// Store the promise to be reused until it resolves
// to a specific value
@@ -368,15 +459,12 @@ SvnResolver.branches = function (source) {
return value;
};
SvnResolver.parseSubversionListOutput = function (stout) {
SvnResolver.parseSubversionListOutput = function(stout) {
var entries = {};
var lines = stout
.trim()
.split(/[\r\n]+/);
var lines = stout.trim().split(/[\r\n]+/);
// For each line in the refs, match only the branches
lines.forEach(function (line) {
lines.forEach(function(line) {
var match = line.match(/\s+([0-9]+)\s.+\s([\w.$-]+)\//i);
if (match && match[2] !== '.') {
@@ -387,9 +475,9 @@ SvnResolver.parseSubversionListOutput = function (stout) {
return entries;
};
SvnResolver.clearRuntimeCache = function () {
SvnResolver.clearRuntimeCache = function() {
// Reset cache for branches, tags, etc
mout.object.forOwn(SvnResolver._cache, function (lru) {
mout.object.forOwn(SvnResolver._cache, function(lru) {
lru.reset();
});
};

View File

@@ -16,7 +16,7 @@ function UrlResolver(decEndpoint, config, logger) {
// If target was specified, error out
if (this._target !== '*') {
throw createError('URL sources can\'t resolve targets', 'ENORESTARGET');
throw createError("URL sources can't resolve targets", 'ENORESTARGET');
}
// If the name was guessed
@@ -24,7 +24,10 @@ function UrlResolver(decEndpoint, config, logger) {
// Remove the ?xxx part
this._name = this._name.replace(/\?.*$/, '');
// Remove extension
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
this._name = this._name.substr(
0,
this._name.length - path.extname(this._name).length
);
}
this._remote = url.parse(this._source);
@@ -35,11 +38,11 @@ mout.object.mixIn(UrlResolver, Resolver);
// -----------------
UrlResolver.isTargetable = function () {
UrlResolver.isTargetable = function() {
return false;
};
UrlResolver.prototype._hasNew = function (pkgMeta) {
UrlResolver.prototype._hasNew = function(pkgMeta) {
var oldCacheHeaders = pkgMeta._cacheHeaders || {};
var reqHeaders = {};
@@ -54,61 +57,71 @@ UrlResolver.prototype._hasNew = function (pkgMeta) {
}
// Make an HEAD request to the source
return Q.nfcall(request.head, this._source, {
ca: this._config.ca.default,
strictSSL: this._config.strictSsl,
timeout: this._config.timeout,
headers: reqHeaders
})
// Compare new headers with the old ones
.spread(function (response) {
var cacheHeaders;
return (
Q.nfcall(request.head, this._source, {
ca: this._config.ca.default,
strictSSL: this._config.strictSsl,
timeout: this._config.timeout,
headers: reqHeaders
})
// Compare new headers with the old ones
.spread(
function(response) {
var cacheHeaders;
// If the server responded with 303 then the resource
// still has the same ETag
if (response.statusCode === 304) {
return false;
}
// If the server responded with 303 then the resource
// still has the same ETag
if (response.statusCode === 304) {
return false;
}
// If status code is not in the 2xx range,
// then just resolve to true
if (response.statusCode < 200 || response.statusCode >= 300) {
return true;
}
// If status code is not in the 2xx range,
// then just resolve to true
if (
response.statusCode < 200 ||
response.statusCode >= 300
) {
return true;
}
// Fallback to comparing cache headers
cacheHeaders = this._collectCacheHeaders(response);
return !mout.object.equals(oldCacheHeaders, cacheHeaders);
}.bind(this), function () {
// Assume new contents if the request failed
// Note that we do not retry the request using the "request-replay" module
// because it would take too long
return true;
});
// Fallback to comparing cache headers
cacheHeaders = this._collectCacheHeaders(response);
return !mout.object.equals(oldCacheHeaders, cacheHeaders);
}.bind(this),
function() {
// Assume new contents if the request failed
// Note that we do not retry the request using the "request-replay" module
// because it would take too long
return true;
}
)
);
};
// TODO: There's room for improvement by using streams if the URL
// is an archive file, by piping read stream to the zip extractor
// This will likely increase the complexity of code but might worth it
UrlResolver.prototype._resolve = function () {
UrlResolver.prototype._resolve = function() {
// Download
return this._download()
// Parse headers
.spread(this._parseHeaders.bind(this))
// Extract file
.spread(this._extract.bind(this))
// Rename file to index
.then(this._rename.bind(this));
return (
this._download()
// Parse headers
.spread(this._parseHeaders.bind(this))
// Extract file
.spread(this._extract.bind(this))
// Rename file to index
.then(this._rename.bind(this))
);
};
// -----------------
UrlResolver.prototype._parseSourceURL = function (_url) {
UrlResolver.prototype._parseSourceURL = function(_url) {
return url.parse(path.basename(_url)).pathname;
};
UrlResolver.prototype._download = function () {
UrlResolver.prototype._download = function() {
var fileName = this._parseSourceURL(this._source);
if (!fileName) {
@@ -136,32 +149,43 @@ UrlResolver.prototype._download = function () {
timeout: this._config.timeout,
headers: reqHeaders
})
.progress(function (state) {
var msg;
.progress(function(state) {
var msg;
// Retry?
if (state.retry) {
msg = 'Download of ' + that._source + ' failed' + (state.error.code ? ' with ' + state.error.code : '') + ', ';
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
that._logger.debug('error', state.error.message, { error: state.error });
return that._logger.warn('retry', msg);
}
// Retry?
if (state.retry) {
msg =
'Download of ' +
that._source +
' failed' +
(state.error.code ? ' with ' + state.error.code : '') +
', ';
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
that._logger.debug('error', state.error.message, {
error: state.error
});
return that._logger.warn('retry', msg);
}
// Progress
msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
if (state.total) {
msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
msg += state.percent + '%';
}
that._logger.info('progress', msg);
})
.then(function (response) {
that._response = response;
return [file, response];
});
// Progress
msg =
'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
if (state.total) {
msg +=
' of ' +
(state.total / 1024 / 1024).toFixed(1) +
'MB downloaded, ';
msg += state.percent + '%';
}
that._logger.info('progress', msg);
})
.then(function(response) {
that._response = response;
return [file, response];
});
};
UrlResolver.prototype._parseHeaders = function (file, response) {
UrlResolver.prototype._parseHeaders = function(file, response) {
var disposition;
var newFile;
var match;
@@ -196,20 +220,19 @@ UrlResolver.prototype._parseHeaders = function (file, response) {
newFile = path.join(this._tempDir, newFile);
return Q.nfcall(fs.rename, file, newFile)
.then(function () {
return Q.nfcall(fs.rename, file, newFile).then(function() {
return [newFile, response];
});
};
UrlResolver.prototype._extract = function (file, response) {
UrlResolver.prototype._extract = function(file, response) {
var mimeType = response.headers['content-type'];
if (mimeType) {
// Clean everything after ; and trim the end result
mimeType = mimeType.split(';')[0].trim();
// Some servers add quotes around the content-type, so we trim that also
mimeType = mout.string.trim(mimeType, ['"', '\'']);
mimeType = mout.string.trim(mimeType, ['"', "'"]);
}
if (!extract.canExtract(file, mimeType)) {
@@ -226,36 +249,42 @@ UrlResolver.prototype._extract = function (file, response) {
});
};
UrlResolver.prototype._rename = function () {
return Q.nfcall(fs.readdir, this._tempDir)
.then(function (files) {
var file;
var oldPath;
var newPath;
UrlResolver.prototype._rename = function() {
return Q.nfcall(fs.readdir, this._tempDir).then(
function(files) {
var file;
var oldPath;
var newPath;
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
// Only rename if there's only one file and it's not the json
if (files.length === 1 && !/^(component|bower)\.json$/.test(files[0])) {
file = files[0];
this._singleFile = 'index' + path.extname(file);
oldPath = path.join(this._tempDir, file);
newPath = path.join(this._tempDir, this._singleFile);
// Only rename if there's only one file and it's not the json
if (
files.length === 1 &&
!/^(component|bower)\.json$/.test(files[0])
) {
file = files[0];
this._singleFile = 'index' + path.extname(file);
oldPath = path.join(this._tempDir, file);
newPath = path.join(this._tempDir, this._singleFile);
return Q.nfcall(fs.rename, oldPath, newPath);
}
}.bind(this));
return Q.nfcall(fs.rename, oldPath, newPath);
}
}.bind(this)
);
};
UrlResolver.prototype._savePkgMeta = function (meta) {
UrlResolver.prototype._savePkgMeta = function(meta) {
// Store collected headers in the package meta
meta._cacheHeaders = this._collectCacheHeaders(this._response);
// Store ETAG under _release
if (meta._cacheHeaders.ETag) {
meta._release = 'e-tag:' + mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
meta._release =
'e-tag:' +
mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
}
// Store main if is a single file
@@ -266,11 +295,11 @@ UrlResolver.prototype._savePkgMeta = function (meta) {
return Resolver.prototype._savePkgMeta.call(this, meta);
};
UrlResolver.prototype._collectCacheHeaders = function (res) {
UrlResolver.prototype._collectCacheHeaders = function(res) {
var headers = {};
// Collect cache headers
this.constructor._cacheHeaders.forEach(function (name) {
this.constructor._cacheHeaders.forEach(function(name) {
var value = res.headers[name.toLowerCase()];
if (value != null) {

View File

@@ -12,7 +12,12 @@ function pluginResolverFactory(resolverFactory, bower) {
bower = bower || {};
if (typeof resolverFactory !== 'function') {
throw createError('Resolver has "' + typeof resolverFactory + '" type instead of "function" type.', 'ERESOLERAPI');
throw createError(
'Resolver has "' +
typeof resolverFactory +
'" type instead of "function" type.',
'ERESOLERAPI'
);
}
var resolver = resolverFactory(bower);
@@ -20,7 +25,9 @@ function pluginResolverFactory(resolverFactory, bower) {
function maxSatisfyingVersion(versions, target) {
var versionsArr, index;
versionsArr = versions.map(function (obj) { return obj.version; });
versionsArr = versions.map(function(obj) {
return obj.version;
});
// Find a satisfying version, enabling strict match so that pre-releases
// have lower priority over normal ones when target is *
@@ -36,7 +43,7 @@ function pluginResolverFactory(resolverFactory, bower) {
}
// @private
PluginResolver.prototype.getEndpoint = function () {
PluginResolver.prototype.getEndpoint = function() {
return object.merge(this._decEndpoint, {
name: this.getName(),
source: this.getSource(),
@@ -44,15 +51,15 @@ function pluginResolverFactory(resolverFactory, bower) {
});
};
PluginResolver.prototype.getSource = function () {
PluginResolver.prototype.getSource = function() {
return this._decEndpoint.source;
};
PluginResolver.prototype.getTarget = function () {
PluginResolver.prototype.getTarget = function() {
return this._decEndpoint.target || '*';
};
PluginResolver.prototype.getName = function () {
PluginResolver.prototype.getName = function() {
if (!this._decEndpoint.name && typeof resolver.getName === 'function') {
return resolver.getName.call(resolver, this.getSource());
} else if (!this._decEndpoint.name) {
@@ -62,7 +69,7 @@ function pluginResolverFactory(resolverFactory, bower) {
}
};
PluginResolver.prototype.getPkgMeta = function () {
PluginResolver.prototype.getPkgMeta = function() {
return this._pkgMeta;
};
@@ -70,32 +77,32 @@ function pluginResolverFactory(resolverFactory, bower) {
// Plugin Resolver is always considered potentially cacheable
// The "resolve" method decides whether to use cached or fetch new version.
PluginResolver.prototype.isCacheable = function () {
PluginResolver.prototype.isCacheable = function() {
return true;
};
// Not only it's always potentially cacheable, but also always potenially new.
// The "resolve" handles logic of re-downloading target if needed.
PluginResolver.prototype.hasNew = function (pkgMeta) {
PluginResolver.prototype.hasNew = function(pkgMeta) {
if (this.hasNewPromise) {
return this.hasNewPromise;
}
this._pkgMeta = pkgMeta;
return this.hasNewPromise = this.resolve().then(function (result) {
return (this.hasNewPromise = this.resolve().then(function(result) {
return result !== undefined;
});
}));
};
PluginResolver.prototype.resolve = function () {
PluginResolver.prototype.resolve = function() {
if (this.resolvePromise) {
return this.resolvePromise;
}
var that = this;
return this.resolvePromise = Q.fcall(function () {
return (this.resolvePromise = Q.fcall(function() {
var target = that.getTarget();
// It means that we can accept ranges as targets
@@ -103,15 +110,19 @@ function pluginResolverFactory(resolverFactory, bower) {
that._release = target;
if (semver.validRange(target)) {
return Q.fcall(resolver.releases.bind(resolver), that.getSource())
.then(function (result) {
return Q.fcall(
resolver.releases.bind(resolver),
that.getSource()
).then(function(result) {
if (!result) {
throw createError('Resolver did not provide releases of package.');
throw createError(
'Resolver did not provide releases of package.'
);
}
var releases = this._releases = result;
var releases = (that._releases = result);
var versions = releases.filter(function (target) {
var versions = releases.filter(function(target) {
return semver.clean(target.version);
});
@@ -119,90 +130,122 @@ function pluginResolverFactory(resolverFactory, bower) {
if (maxRelease) {
that._version = maxRelease.version;
that._release = that._decEndpoint.target = maxRelease.target;
that._release = that._decEndpoint.target =
maxRelease.target;
} else {
throw createError('No version found that was able to satisfy ' + target, 'ENORESTARGET', {
details: !versions.length ?
'No versions found in ' + that.getSource() :
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
});
throw createError(
'No version found that was able to satisfy ' +
target,
'ENORESTARGET',
{
details: !versions.length
? 'No versions found in ' +
that.getSource()
: 'Available versions: ' +
versions
.map(function(version) {
return version.version;
})
.join(', ')
}
);
}
});
}
} else {
if (semver.validRange(target) && target !== '*') {
return Q.reject(createError('Resolver does not accept version ranges (' + target + ')'));
return Q.reject(
createError(
'Resolver does not accept version ranges (' +
target +
')'
)
);
}
}
})
.then(function () {
// We pass old _resolution (if hasNew has been called before contents).
// So resolver can decide wheter use cached version of contents new one.
if (typeof resolver.fetch !== 'function') {
throw createError('Resolver does not implement the "fetch" method.');
}
var cached = {};
if (that._releases) {
cached.releases = that._releases;
}
if (that._pkgMeta) {
cached.endpoint = {
name: that._pkgMeta.name,
source: that._pkgMeta._source,
target: that._pkgMeta._target
};
cached.release = that._pkgMeta._release;
cached.version = that._pkgMeta.version;
cached.resolution = that._pkgMeta._resolution || {};
}
return Q.fcall(resolver.fetch.bind(resolver), that.getEndpoint(), cached);
})
.then(function (result) {
// Empty result means to re-use existing resolution
if (!result) {
return;
} else {
if (!result.tempPath) {
throw createError('Resolver did not provide path to extracted contents of package.');
.then(function() {
// We pass old _resolution (if hasNew has been called before contents).
// So resolver can decide wheter use cached version of contents new one.
if (typeof resolver.fetch !== 'function') {
throw createError(
'Resolver does not implement the "fetch" method.'
);
}
that._tempDir = result.tempPath;
var cached = {};
return that._readJson(that._tempDir).then(function (meta) {
return that._applyPkgMeta(meta, result)
.then(that._savePkgMeta.bind(that, meta, result))
.then(function () {
return that._tempDir;
if (that._releases) {
cached.releases = that._releases;
}
if (that._pkgMeta) {
cached.endpoint = {
name: that._pkgMeta.name,
source: that._pkgMeta._source,
target: that._pkgMeta._target
};
cached.release = that._pkgMeta._release;
cached.version = that._pkgMeta.version;
cached.resolution = that._pkgMeta._resolution || {};
}
return Q.fcall(
resolver.fetch.bind(resolver),
that.getEndpoint(),
cached
);
})
.then(function(result) {
// Empty result means to re-use existing resolution
if (!result) {
return;
} else {
if (!result.tempPath) {
throw createError(
'Resolver did not provide path to extracted contents of package.'
);
}
that._tempDir = result.tempPath;
return that._readJson(that._tempDir).then(function(meta) {
return that
._applyPkgMeta(meta, result)
.then(that._savePkgMeta.bind(that, meta, result))
.then(function() {
return that._tempDir;
});
});
});
}
});
}
}));
};
PluginResolver.prototype._readJson = function (dir) {
PluginResolver.prototype._readJson = function(dir) {
var that = this;
return readJson(dir, {
assume: { name: that.getName() }
})
.spread(function (json, deprecated) {
assume: { name: that.getName() },
logger: bower.logger
}).spread(function(json, deprecated) {
if (deprecated) {
bower.logger.warn('deprecated', 'Package ' + that.getName() + ' is using the deprecated ' + deprecated);
bower.logger.warn(
'deprecated',
'Package ' +
that.getName() +
' is using the deprecated ' +
deprecated
);
}
return json;
});
};
PluginResolver.prototype._applyPkgMeta = function (meta, result) {
PluginResolver.prototype._applyPkgMeta = function(meta, result) {
// Check if name defined in the json is different
// If so and if the name was "guessed", assume the json name
if (meta.name !== this._name) {
@@ -211,17 +254,21 @@ function pluginResolverFactory(resolverFactory, bower) {
// Handle ignore property, deleting all files from the temporary directory
// If no ignores were specified, simply resolve
if (result.removeIgnores === false || !meta.ignore || !meta.ignore.length) {
if (
result.removeIgnores === false ||
!meta.ignore ||
!meta.ignore.length
) {
return Q.resolve(meta);
}
// Otherwise remove them from the temp dir
return removeIgnores(this._tempDir, meta).then(function () {
return removeIgnores(this._tempDir, meta).then(function() {
return meta;
});
};
PluginResolver.prototype._savePkgMeta = function (meta, result) {
PluginResolver.prototype._savePkgMeta = function(meta, result) {
var that = this;
meta._source = that.getSource();
@@ -244,30 +291,37 @@ function pluginResolverFactory(resolverFactory, bower) {
// Stringify contents
var contents = JSON.stringify(meta, null, 2);
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
.then(function () {
return that._pkgMeta = meta;
return Q.nfcall(
fs.writeFile,
path.join(this._tempDir, '.bower.json'),
contents
).then(function() {
return (that._pkgMeta = meta);
});
};
// It is used only by "bower info". It returns all semver versions.
PluginResolver.versions = function (source) {
return Q.fcall(resolver.releases.bind(resolver), source).then(function (result) {
PluginResolver.versions = function(source) {
return Q.fcall(resolver.releases.bind(resolver), source).then(function(
result
) {
if (!result) {
throw createError('Resolver did not provide releases of package.');
throw createError(
'Resolver did not provide releases of package.'
);
}
var releases = this._releases = result;
var releases = (this._releases = result);
var versions = releases.map(function (version) {
var versions = releases.map(function(version) {
return semver.clean(version.version);
});
versions = versions.filter(function (version) {
versions = versions.filter(function(version) {
return version;
});
versions.sort(function (a, b) {
versions.sort(function(a, b) {
return semver.rcompare(a, b);
});
@@ -275,39 +329,50 @@ function pluginResolverFactory(resolverFactory, bower) {
});
};
PluginResolver.isTargetable = function () {
PluginResolver.isTargetable = function() {
// If resolver doesn't define versions function, it's not targetable..
return typeof resolver.releases === 'function';
};
PluginResolver.clearRuntimeCache = function () {
PluginResolver.clearRuntimeCache = function() {
resolver = resolverFactory(bower);
};
PluginResolver.match = function (source) {
PluginResolver.match = function(source) {
if (typeof resolver.match !== 'function') {
throw createError('Resolver is missing "match" method.', 'ERESOLVERAPI');
throw createError(
'Resolver is missing "match" method.',
'ERESOLVERAPI'
);
}
var match = resolver.match.bind(resolver);
return Q.fcall(match, source).then(function (result) {
return Q.fcall(match, source).then(function(result) {
if (typeof result !== 'boolean') {
throw createError('Resolver\'s "match" method should return a boolean', 'ERESOLVERAPI');
throw createError(
'Resolver\'s "match" method should return a boolean',
'ERESOLVERAPI'
);
}
return result;
});
};
PluginResolver.locate = function (source) {
PluginResolver.locate = function(source) {
if (typeof resolver.locate !== 'function') {
return source;
}
return Q.fcall(resolver.locate.bind(resolver), source).then(function (result) {
return Q.fcall(resolver.locate.bind(resolver), source).then(function(
result
) {
if (typeof result !== 'string') {
throw createError('Resolver\'s "locate" method should return a string', 'ERESOLVERAPI');
throw createError(
'Resolver\'s "locate" method should return a string',
'ERESOLVERAPI'
);
}
return result;
@@ -317,6 +382,4 @@ function pluginResolverFactory(resolverFactory, bower) {
return PluginResolver;
}
module.exports = pluginResolverFactory;

View File

@@ -3,28 +3,36 @@ var cmd = require('../util/cmd');
var Q = require('q');
var shellquote = require('shell-quote');
var orderByDependencies = function (packages, installed, json) {
var orderByDependencies = function(packages, installed, json) {
var ordered = [];
installed = mout.object.keys(installed);
var depsSatisfied = function (packageName) {
return mout.array.difference(mout.object.keys(packages[packageName].dependencies), installed, ordered).length === 0;
var depsSatisfied = function(packageName) {
return (
mout.array.difference(
mout.object.keys(packages[packageName].dependencies),
installed,
ordered
).length === 0
);
};
var depsFromBowerJson = json && json.dependencies ? mout.object.keys(json.dependencies) : [];
var depsFromBowerJson =
json && json.dependencies ? mout.object.keys(json.dependencies) : [];
var packageNames = mout.object.keys(packages);
//get the list of the packages that are specified in bower.json in that order
//its nice to maintain that order for users
var desiredOrder = mout.array.intersection(depsFromBowerJson, packageNames);
//then add to the end any remaining packages that werent in bower.json
desiredOrder = desiredOrder.concat(mout.array.difference(packageNames, desiredOrder));
desiredOrder = desiredOrder.concat(
mout.array.difference(packageNames, desiredOrder)
);
//the desired order isn't necessarily a correct dependency specific order
//so we ensure that below
var resolvedOne = true;
while (resolvedOne) {
resolvedOne = false;
for (var i = 0; i < desiredOrder.length; i++) {
@@ -43,17 +51,16 @@ var orderByDependencies = function (packages, installed, json) {
//so lets just jam those names on the end
ordered = ordered.concat(desiredOrder);
}
}
return ordered;
};
var run = function (cmdString, action, logger, config) {
var run = function(cmdString, action, logger, config) {
logger.action(action, cmdString);
//pass env + BOWER_PID so callees can identify a preinstall+postinstall from the same bower instance
var env = mout.object.mixIn({ 'BOWER_PID': process.pid }, process.env);
var env = mout.object.mixIn({ BOWER_PID: process.pid }, process.env);
var args = shellquote.parse(cmdString, env);
var cmdName = args[0];
mout.array.remove(args, cmdName); //no rest() in mout
@@ -65,8 +72,8 @@ var run = function (cmdString, action, logger, config) {
var promise = cmd(cmdName, args, options);
promise.progress(function (progress) {
progress.split('\n').forEach(function (line) {
promise.progress(function(progress) {
progress.split('\n').forEach(function(line) {
if (line) {
logger.action(action, line);
}
@@ -76,19 +83,38 @@ var run = function (cmdString, action, logger, config) {
return promise;
};
var hook = function (action, ordered, config, logger, packages, installed, json) {
if (mout.object.keys(packages).length === 0 || !config.scripts || !config.scripts[action]) {
var hook = function(
action,
ordered,
config,
logger,
packages,
installed,
json
) {
if (
mout.object.keys(packages).length === 0 ||
!config.scripts ||
!config.scripts[action]
) {
return Q();
}
var orderedPackages = ordered ? orderByDependencies(packages, installed, json) : mout.object.keys(packages);
var orderedPackages = ordered
? orderByDependencies(packages, installed, json)
: mout.object.keys(packages);
var placeholder = new RegExp('%', 'g');
var cmdString = mout.string.replace(config.scripts[action], placeholder, orderedPackages.join(' '));
var cmdString = mout.string.replace(
config.scripts[action],
placeholder,
orderedPackages.join(' ')
);
return run(cmdString, action, logger, config);
};
module.exports = {
preuninstall: mout.function.partial(hook, 'preuninstall', false),
postuninstall: mout.function.partial(hook, 'postuninstall', false),
preinstall: mout.function.partial(hook, 'preinstall', true),
postinstall: mout.function.partial(hook, 'postinstall', true),
//only exposed for test

View File

@@ -7,7 +7,7 @@ function JsonRenderer() {
this._nrLogs = 0;
}
JsonRenderer.prototype.end = function (data) {
JsonRenderer.prototype.end = function(data) {
if (this._nrLogs) {
process.stderr.write(']\n');
}
@@ -17,7 +17,7 @@ JsonRenderer.prototype.end = function (data) {
}
};
JsonRenderer.prototype.error = function (err) {
JsonRenderer.prototype.error = function(err) {
var message = err.message;
var stack;
@@ -32,13 +32,13 @@ JsonRenderer.prototype.error = function (err) {
// Stack
stack = err.fstream_stack || err.stack || 'N/A';
err.stacktrace = (Array.isArray(stack) ? stack.join('\n') : stack);
err.stacktrace = Array.isArray(stack) ? stack.join('\n') : stack;
this.log(err);
this.end();
};
JsonRenderer.prototype.log = function (log) {
JsonRenderer.prototype.log = function(log) {
if (!this._nrLogs) {
process.stderr.write('[');
} else {
@@ -49,12 +49,12 @@ JsonRenderer.prototype.log = function (log) {
this._nrLogs++;
};
JsonRenderer.prototype.prompt = function (prompts) {
JsonRenderer.prototype.prompt = function(prompts) {
var promise = Q.resolve();
var answers = {};
var that = this;
prompts.forEach(function (prompt) {
prompts.forEach(function(prompt) {
var opts;
var funcName;
@@ -63,65 +63,68 @@ JsonRenderer.prototype.prompt = function (prompts) {
// Prompt
opts = {
silent: true, // To not mess with JSON output
trim: false, // To allow " " to not assume the default value
default: prompt.default == null ? '' : prompt.default, // If default is null, make it '' so that it does not retry
validator: !prompt.validate ? null : function (value) {
var ret = prompt.validate(value);
silent: true, // To not mess with JSON output
trim: false, // To allow " " to not assume the default value
default: prompt.default == null ? '' : prompt.default, // If default is null, make it '' so that it does not retry
validator: !prompt.validate
? null
: function(value) {
var ret = prompt.validate(value);
if (typeof ret === 'string') {
throw ret;
}
if (typeof ret === 'string') {
throw ret;
}
return value;
},
return value;
}
};
// For now only "input", "confirm" and "password" are supported
switch (prompt.type) {
case 'input':
funcName = 'prompt';
break;
case 'confirm':
case 'password':
funcName = prompt.type;
break;
case 'checkbox':
funcName = 'prompt';
break;
default:
promise = promise.then(function () {
throw createError('Unknown prompt type', 'ENOTSUP');
});
return;
case 'input':
funcName = 'prompt';
break;
case 'confirm':
case 'password':
funcName = prompt.type;
break;
case 'checkbox':
funcName = 'prompt';
break;
default:
promise = promise.then(function() {
throw createError('Unknown prompt type', 'ENOTSUP');
});
return;
}
promise = promise.then(function () {
promise = promise.then(function() {
// Log
prompt.level = 'prompt';
that.log(prompt);
return Q.nfcall(promptly[funcName], '', opts)
.then(function (answer) {
return Q.nfcall(promptly[funcName], '', opts).then(function(
answer
) {
answers[prompt.name] = answer;
});
});
if (prompt.type === 'checkbox') {
promise = promise.then(function () {
promise = promise.then(function() {
answers[prompt.name] = answers[prompt.name].split(',');
});
}
});
return promise.then(function () {
return promise.then(function() {
return answers;
});
};
// -------------------------
JsonRenderer.prototype._stringify = function (log) {
JsonRenderer.prototype._stringify = function(log) {
// To json
var str = JSON.stringify(log, null, ' ');
// Remove colors in case some log has colors..

View File

@@ -11,9 +11,9 @@ var template = require('../util/template');
function StandardRenderer(command, config) {
this._sizes = {
id: 13, // Id max chars
id: 13, // Id max chars
label: 20, // Label max chars
sumup: 5 // Amount to sum when the label exceeds
sumup: 5 // Amount to sum when the label exceeds
};
this._colors = {
warn: chalk.yellow,
@@ -32,7 +32,7 @@ function StandardRenderer(command, config) {
this._compact = process.stdout.columns < 120;
}
var exitOnPipeError = function (err) {
var exitOnPipeError = function(err) {
if (err.code === 'EPIPE') {
process.exit(0);
}
@@ -43,7 +43,7 @@ function StandardRenderer(command, config) {
process.stderr.on('error', exitOnPipeError);
}
StandardRenderer.prototype.end = function (data) {
StandardRenderer.prototype.end = function(data) {
var method = '_' + mout.string.camelCase(this._command);
if (this[method]) {
@@ -51,7 +51,7 @@ StandardRenderer.prototype.end = function (data) {
}
};
StandardRenderer.prototype.error = function (err) {
StandardRenderer.prototype.error = function(err) {
var str;
var stack;
@@ -60,12 +60,19 @@ StandardRenderer.prototype.error = function (err) {
err.id = err.code || 'error';
err.level = 'error';
str = this._prefix(err) + ' ' + err.message.replace(/\r?\n/g, ' ').trim() + '\n';
str =
this._prefix(err) +
' ' +
err.message.replace(/\r?\n/g, ' ').trim() +
'\n';
this._write(process.stderr, 'bower ' + str);
// Check if additional details were provided
if (err.details) {
str = chalk.yellow('\nAdditional error details:\n') + err.details.trim() + '\n';
str =
chalk.yellow('\nAdditional error details:\n') +
err.details.trim() +
'\n';
this._write(process.stderr, str);
}
@@ -83,12 +90,18 @@ StandardRenderer.prototype.error = function (err) {
// Print bower version, node version and system info.
this._write(process.stderr, chalk.yellow('\nSystem info:\n'));
this._write(process.stderr, 'Bower version: ' + version + '\n');
this._write(process.stderr, 'Node version: ' + process.versions.node + '\n');
this._write(process.stderr, 'OS: ' + os.type() + ' ' + os.release() + ' ' + os.arch() + '\n');
this._write(
process.stderr,
'Node version: ' + process.versions.node + '\n'
);
this._write(
process.stderr,
'OS: ' + os.type() + ' ' + os.release() + ' ' + os.arch() + '\n'
);
}
};
StandardRenderer.prototype.log = function (log) {
StandardRenderer.prototype.log = function(log) {
var method = '_' + mout.string.camelCase(log.id) + 'Log';
this._guessOrigin(log);
@@ -101,12 +114,12 @@ StandardRenderer.prototype.log = function (log) {
}
};
StandardRenderer.prototype.prompt = function (prompts) {
StandardRenderer.prototype.prompt = function(prompts) {
var deferred;
// Strip colors from the prompt if color is disabled
if (!this._config.color) {
prompts.forEach(function (prompt) {
prompts.forEach(function(prompt) {
prompt.message = chalk.stripColor(prompt.message);
});
}
@@ -121,7 +134,7 @@ StandardRenderer.prototype.prompt = function (prompts) {
// -------------------------
StandardRenderer.prototype._help = function (data) {
StandardRenderer.prototype._help = function(data) {
var str;
var that = this;
var specific;
@@ -136,60 +149,68 @@ StandardRenderer.prototype._help = function (data) {
if (template.exists(specific)) {
str = template.render(specific, data);
} else {
str = template.render('std/help-generic.std', data);
str = template.render('std/help-generic.std', data);
}
that._write(process.stdout, str);
}
};
StandardRenderer.prototype._install = function (packages) {
StandardRenderer.prototype._install = function(packages) {
var str = '';
mout.object.forOwn(packages, function (pkg) {
var cliTree;
mout.object.forOwn(
packages,
function(pkg) {
var cliTree;
// List only 1 level deep dependencies
mout.object.forOwn(pkg.dependencies, function (dependency) {
dependency.dependencies = {};
});
// Make canonical dir relative
pkg.canonicalDir = path.relative(this._config.cwd, pkg.canonicalDir);
// Signal as root
pkg.root = true;
// List only 1 level deep dependencies
mout.object.forOwn(pkg.dependencies, function(dependency) {
dependency.dependencies = {};
});
// Make canonical dir relative
pkg.canonicalDir = path.relative(
this._config.cwd,
pkg.canonicalDir
);
// Signal as root
pkg.root = true;
cliTree = this._tree2archy(pkg);
str += '\n' + archy(cliTree);
}, this);
cliTree = this._tree2archy(pkg);
str += '\n' + archy(cliTree);
},
this
);
if (str) {
this._write(process.stdout, str);
}
};
StandardRenderer.prototype._update = function (packages) {
StandardRenderer.prototype._update = function(packages) {
this._install(packages);
};
StandardRenderer.prototype._list = function (tree) {
StandardRenderer.prototype._list = function(tree) {
var cliTree;
if (tree.pkgMeta) {
tree.root = true;
cliTree = archy(this._tree2archy(tree));
} else {
cliTree = stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
cliTree =
stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
}
this._write(process.stdout, cliTree);
};
StandardRenderer.prototype._search = function (results) {
StandardRenderer.prototype._search = function(results) {
var str = template.render('std/search-results.std', results);
this._write(process.stdout, str);
};
StandardRenderer.prototype._info = function (data) {
StandardRenderer.prototype._info = function(data) {
var str = '';
var pkgMeta = data;
var includeVersions = false;
@@ -212,7 +233,7 @@ StandardRenderer.prototype._info = function (data) {
data.numPreReleases = 0;
// If output isn't verbose, hide prereleases
if (!this._config.verbose) {
data.versions = mout.array.filter(data.versions, function (version) {
data.versions = mout.array.filter(data.versions, function(version) {
version = semverUtils.parse(version);
if (!version.release && !version.build) {
return true;
@@ -227,13 +248,13 @@ StandardRenderer.prototype._info = function (data) {
this._write(process.stdout, str);
};
StandardRenderer.prototype._lookup = function (data) {
StandardRenderer.prototype._lookup = function(data) {
var str = template.render('std/lookup.std', data);
this._write(process.stdout, str);
};
StandardRenderer.prototype._link = function (data) {
StandardRenderer.prototype._link = function(data) {
this._sizes.id = 4;
this.log({
@@ -248,7 +269,7 @@ StandardRenderer.prototype._link = function (data) {
}
};
StandardRenderer.prototype._register = function (data) {
StandardRenderer.prototype._register = function(data) {
var str;
// If no data passed, it means the user aborted
@@ -260,17 +281,20 @@ StandardRenderer.prototype._register = function (data) {
this._write(process.stdout, str);
};
StandardRenderer.prototype._cacheList = function (entries) {
entries.forEach(function (entry) {
StandardRenderer.prototype._cacheList = function(entries) {
entries.forEach(function(entry) {
var pkgMeta = entry.pkgMeta;
var version = pkgMeta.version || pkgMeta._target;
this._write(process.stdout, pkgMeta.name + '=' + pkgMeta._source + '#' + version + '\n');
this._write(
process.stdout,
pkgMeta.name + '=' + pkgMeta._source + '#' + version + '\n'
);
}, this);
};
// -------------------------
StandardRenderer.prototype._genericLog = function (log) {
StandardRenderer.prototype._genericLog = function(log) {
var stream;
var str;
@@ -284,7 +308,7 @@ StandardRenderer.prototype._genericLog = function (log) {
this._write(stream, 'bower ' + str);
};
StandardRenderer.prototype._checkoutLog = function (log) {
StandardRenderer.prototype._checkoutLog = function(log) {
if (this._compact) {
log.message = log.origin.split('#')[0] + '#' + log.message;
}
@@ -292,7 +316,7 @@ StandardRenderer.prototype._checkoutLog = function (log) {
this._genericLog(log);
};
StandardRenderer.prototype._progressLog = function (log) {
StandardRenderer.prototype._progressLog = function(log) {
if (this._compact) {
log.message = log.origin + ' ' + log.message;
}
@@ -300,7 +324,7 @@ StandardRenderer.prototype._progressLog = function (log) {
this._genericLog(log);
};
StandardRenderer.prototype._extractLog = function (log) {
StandardRenderer.prototype._extractLog = function(log) {
if (this._compact) {
log.message = log.origin + ' ' + log.message;
}
@@ -308,19 +332,23 @@ StandardRenderer.prototype._extractLog = function (log) {
this._genericLog(log);
};
StandardRenderer.prototype._incompatibleLog = function (log) {
StandardRenderer.prototype._incompatibleLog = function(log) {
var str;
var templatePath;
// Generate dependants string for each pick
log.data.picks.forEach(function (pick) {
pick.dependants = pick.dependants.map(function (dependant) {
var release = dependant.pkgMeta._release;
return dependant.endpoint.name + (release ? '#' + release : '');
}).join(', ');
log.data.picks.forEach(function(pick) {
pick.dependants = pick.dependants
.map(function(dependant) {
var release = dependant.pkgMeta._release;
return dependant.endpoint.name + (release ? '#' + release : '');
})
.join(', ');
});
templatePath = log.data.suitable ? 'std/conflict-resolved.std' : 'std/conflict.std';
templatePath = log.data.suitable
? 'std/conflict-resolved.std'
: 'std/conflict.std';
str = template.render(templatePath, log.data);
this._write(process.stdout, '\n');
@@ -328,15 +356,18 @@ StandardRenderer.prototype._incompatibleLog = function (log) {
this._write(process.stdout, '\n');
};
StandardRenderer.prototype._solvedLog = function (log) {
StandardRenderer.prototype._solvedLog = function(log) {
this._incompatibleLog(log);
};
StandardRenderer.prototype._jsonLog = function (log) {
this._write(process.stdout, '\n' + this._highlightJson(log.data.json) + '\n\n');
StandardRenderer.prototype._jsonLog = function(log) {
this._write(
process.stdout,
'\n' + this._highlightJson(log.data.json) + '\n\n'
);
};
StandardRenderer.prototype._cachedEntryLog = function (log) {
StandardRenderer.prototype._cachedEntryLog = function(log) {
if (this._compact) {
log.message = log.origin;
}
@@ -346,7 +377,7 @@ StandardRenderer.prototype._cachedEntryLog = function (log) {
// -------------------------
StandardRenderer.prototype._guessOrigin = function (log) {
StandardRenderer.prototype._guessOrigin = function(log) {
var data = log.data;
if (!data) {
@@ -354,7 +385,8 @@ StandardRenderer.prototype._guessOrigin = function (log) {
}
if (data.endpoint) {
log.origin = data.endpoint.name || (data.registry && data.endpoint.source);
log.origin =
data.endpoint.name || (data.registry && data.endpoint.source);
// Resort to using the resolver name for unnamed endpoints
if (!log.origin && data.resolver) {
@@ -373,7 +405,7 @@ StandardRenderer.prototype._guessOrigin = function (log) {
}
};
StandardRenderer.prototype._prefix = function (log) {
StandardRenderer.prototype._prefix = function(log) {
var label;
var length;
var nrSpaces;
@@ -405,7 +437,7 @@ StandardRenderer.prototype._prefix = function (log) {
return chalk.green(label) + mout.string.repeat(' ', nrSpaces) + idColor(id);
};
StandardRenderer.prototype._write = function (stream, str) {
StandardRenderer.prototype._write = function(stream, str) {
if (!this._config.color) {
str = chalk.stripColor(str);
}
@@ -413,18 +445,18 @@ StandardRenderer.prototype._write = function (stream, str) {
stream.write(str);
};
StandardRenderer.prototype._highlightJson = function (json) {
StandardRenderer.prototype._highlightJson = function(json) {
var cardinal = require('cardinal');
return cardinal.highlight(stringifyObject(json, { indent: ' ' }), {
theme: {
String: {
_default: function (str) {
_default: function(str) {
return chalk.cyan(str);
}
},
Identifier: {
_default: function (str) {
_default: function(str) {
return chalk.green(str);
}
}
@@ -433,9 +465,11 @@ StandardRenderer.prototype._highlightJson = function (json) {
});
};
StandardRenderer.prototype._tree2archy = function (node) {
StandardRenderer.prototype._tree2archy = function(node) {
var dependencies = mout.object.values(node.dependencies);
var version = !node.missing ? node.pkgMeta._release || node.pkgMeta.version : null;
var version = !node.missing
? node.pkgMeta._release || node.pkgMeta.version
: null;
var label = node.endpoint.name + (version ? '#' + version : '');
var update;
@@ -458,7 +492,8 @@ StandardRenderer.prototype._tree2archy = function (node) {
}
if (node.incompatible) {
label += chalk.yellow(' incompatible') + ' with ' + node.endpoint.target;
label +=
chalk.yellow(' incompatible') + ' with ' + node.endpoint.target;
} else if (node.extraneous) {
label += chalk.green(' extraneous');
}
@@ -472,7 +507,7 @@ StandardRenderer.prototype._tree2archy = function (node) {
}
if (node.update.latest !== node.update.target) {
update += (update ? ', ' : '');
update += update ? ', ' : '';
update += 'latest is ' + node.update.latest;
}
@@ -500,7 +535,7 @@ StandardRenderer._wideCommands = [
'register'
];
StandardRenderer._idMappings = {
'mutual': 'conflict',
mutual: 'conflict',
'cached-entry': 'cached'
};

View File

@@ -1,17 +1,10 @@
var chalk = require('chalk');
var templateColors = [
'yellow',
'green',
'cyan',
'red',
'white',
'magenta'
];
var templateColors = ['yellow', 'green', 'cyan', 'red', 'white', 'magenta'];
function colors(Handlebars) {
templateColors.forEach(function (color) {
Handlebars.registerHelper(color, function (context) {
templateColors.forEach(function(color) {
Handlebars.registerHelper(color, function(context) {
return chalk[color](context.fn(this));
});
});

View File

@@ -1,9 +1,9 @@
var mout = require('mout');
var leadLinesRegExp = /^\r?\n/;
var multipleLinesRegExp = /\r?\n(\r?\n)+/mg;
var multipleLinesRegExp = /\r?\n(\r?\n)+/gm;
function condense(Handlebars) {
Handlebars.registerHelper('condense', function (context) {
Handlebars.registerHelper('condense', function(context) {
var str = context.fn(this);
// Remove multiple lines

View File

@@ -1,7 +1,7 @@
var mout = require('mout');
function indent(Handlebars) {
Handlebars.registerHelper('indent', function (context) {
Handlebars.registerHelper('indent', function(context) {
var hash = context.hash;
var indentStr = mout.string.repeat(' ', parseInt(hash.level, 10));

View File

@@ -1,7 +1,7 @@
var mout = require('mout');
function rpad(Handlebars) {
Handlebars.registerHelper('rpad', function (context) {
Handlebars.registerHelper('rpad', function(context) {
var hash = context.hash;
var minLength = parseInt(hash.minLength, 10);
var chr = hash.char;

View File

@@ -1,5 +1,5 @@
function sum(Handlebars) {
Handlebars.registerHelper('sum', function (val1, val2) {
Handlebars.registerHelper('sum', function(val1, val2) {
return val1 + val2;
});
}

View File

@@ -1,6 +1,6 @@
{
"command": "lookup",
"description": "Looks up a package URL by name.",
"description": "Look up a single package URL by name.",
"usage": [
"lookup <name> [<options>]"
],

View File

@@ -1,6 +1,6 @@
{
"command": "search",
"description": "Finds all packages or a specific package.",
"description": "Search for packages by name.",
"usage": [
"search <name> [<options>]"
],

View File

@@ -1,8 +1,8 @@
{
"command": "version",
"description": "Run this in a package directory to bump the version and write the new data back to the bower.json file.\n\nThe newversion argument should be a valid semver string, or a valid second argument to semver.inc (one of \"build\", \"patch\", \"minor\", or \"major\"). In the second case, the existing version will be incremented\nby 1 in the specified field.\n\nIf run in a git repo, it will also create a version commit and tag, and fail if the repo is not clean.\n\nIf supplied with --message (shorthand: -m) config option, bower will use it as a commit message when creating a version commit. If the message config contains %s then that will be replaced with the resulting\nversion number. For example:\n\n bower version patch -m \"Upgrade to %s for reasons\"",
"description": "Creates an empty version commit and tag, and fail if the repo is not clean.\n\nThe <version> argument should be a valid semver string, or one of following:\nbuild, patch, minor, major.\n\nIf supplied with --message (shorthand: -m) config option, bower will use it\nas a commit message when creating a version commit. If the message config\ncontains %s then that will be replaced with the resulting version number.\n\nFor example:\n\n bower version patch -m \"Upgrade to %s for reasons\"",
"usage": [
"version [<newversion> | major | minor | patch]"
"version [<version> | major | minor | patch]"
],
"options": [
{

View File

@@ -12,10 +12,10 @@
"link": "Symlink a package folder",
"list": "List local packages - and possible updates",
"login": "Authenticate with GitHub and store credentials",
"lookup": "Look up a package URL by name",
"lookup": "Look up a single package URL by name",
"prune": "Removes local extraneous packages",
"register": "Register a package",
"search": "Search for a package by name",
"search": "Search for packages by name",
"update": "Update a local package",
"uninstall": "Remove a local package",
"unregister": "Remove a package from the registry",
@@ -69,6 +69,10 @@
{
"flag": "--no-color",
"description": "Disable colors"
},
{
"flag": "--config.interactive=false",
"description": "Disable prompts"
}
]
}

View File

@@ -11,7 +11,7 @@ Commands:
{{#condense}}
{{#each commands}}
{{#rpad length="23"}}{{@key}}{{/rpad}} {{.}}
{{#rpad minLength="23"}}{{@key}}{{/rpad}} {{.}}
{{/each}}
{{/condense}}
{{#rpad minLength="23"}}{{/rpad}}

View File

@@ -5,7 +5,7 @@ function expandNames(obj, prefix, stack) {
prefix = prefix || '';
stack = stack || [];
mout.object.forOwn(obj, function (value, name) {
mout.object.forOwn(obj, function(value, name) {
name = prefix + name;
stack.push(name);
@@ -18,7 +18,7 @@ function expandNames(obj, prefix, stack) {
return stack;
}
module.exports = function (commands) {
module.exports = function(commands) {
var abbreviations = abbrev(expandNames(commands));
abbreviations.i = 'install';

View File

@@ -15,10 +15,10 @@ function readOptions(options, argv) {
options = options || {};
}
types = mout.object.map(options, function (option) {
types = mout.object.map(options, function(option) {
return option.type;
});
mout.object.forOwn(options, function (option, name) {
mout.object.forOwn(options, function(option, name) {
shorthands[option.shorthand] = '--' + name;
});
@@ -26,7 +26,7 @@ function readOptions(options, argv) {
// Filter only the specified options because nopt parses every --
// Also make them camel case
mout.object.forOwn(noptOptions, function (value, key) {
mout.object.forOwn(noptOptions, function(value, key) {
if (options[key]) {
parsedOptions[mout.string.camelCase(key)] = value;
}

View File

@@ -37,17 +37,17 @@ function getWindowsCommand(command) {
try {
fullCommand = which.sync(command);
} catch (err) {
return winWhichCache[command] = command;
return (winWhichCache[command] = command);
}
extension = path.extname(fullCommand).toLowerCase();
// Does it need to be converted?
if (winBatchExtensions.indexOf(extension) === -1) {
return winWhichCache[command] = command;
return (winWhichCache[command] = command);
}
return winWhichCache[command] = fullCommand;
return (winWhichCache[command] = fullCommand);
}
// Executes a shell command, buffering the stdout and stderr
@@ -67,25 +67,25 @@ function executeCmd(command, args, options) {
// Buffer output, reporting progress
process = cp.spawn(command, args, options);
process.stdout.on('data', function (data) {
process.stdout.on('data', function(data) {
data = data.toString();
deferred.notify(data);
stdout += data;
});
process.stderr.on('data', function (data) {
process.stderr.on('data', function(data) {
data = data.toString();
deferred.notify(data);
stderr += data;
});
// If there is an error spawning the command, reject the promise
process.on('error', function (error) {
process.on('error', function(error) {
return deferred.reject(error);
});
// Listen to the close event instead of exit
// They are similar but close ensures that streams are flushed
process.on('close', function (code) {
process.on('close', function(code) {
var fullCommand;
var error;
@@ -99,10 +99,19 @@ function executeCmd(command, args, options) {
fullCommand += args.length ? ' ' + args.join(' ') : '';
// Build the error instance
error = createError('Failed to execute "' + fullCommand + '", exit code of #' + code + '\n' + stderr, 'ECMDERR', {
details: stderr,
exitCode: code
});
error = createError(
'Failed to execute "' +
fullCommand +
'", exit code of #' +
code +
'\n' +
stderr,
'ECMDERR',
{
details: stderr,
exitCode: code
}
);
return deferred.reject(error);
}

View File

@@ -26,18 +26,17 @@ function copy(reader, writer) {
deferred = Q.defer();
reader
.on('error', deferred.reject)
// Pipe to writer
.pipe(fstream.Writer(writer))
.on('error', deferred.reject)
.on('close', deferred.resolve);
.on('error', deferred.reject)
// Pipe to writer
.pipe(fstream.Writer(writer))
.on('error', deferred.reject)
.on('close', deferred.resolve);
return deferred.promise;
}
function copyMode(src, dst) {
return Q.nfcall(fs.stat, src)
.then(function (stat) {
return Q.nfcall(fs.stat, src).then(function(stat) {
return Q.nfcall(fs.chmod, dst, stat.mode);
});
}
@@ -68,14 +67,17 @@ function copyFile(src, dst, opts) {
opts = parseOptions(opts);
promise = copy({
path: src,
type: 'File'
}, {
path: dst,
mode: opts.mode,
type: 'File'
});
promise = copy(
{
path: src,
type: 'File'
},
{
path: dst,
mode: opts.mode,
type: 'File'
}
);
if (opts.copyMode) {
promise = promise.then(copyMode.bind(copyMode, src, dst));
@@ -93,15 +95,18 @@ function copyDir(src, dst, opts) {
opts = parseOptions(opts);
promise = copy({
path: src,
type: 'Directory',
ignore: opts.ignore
}, {
path: dst,
mode: opts.mode,
type: 'Directory'
});
promise = copy(
{
path: src,
type: 'Directory',
ignore: opts.ignore
},
{
path: dst,
mode: opts.mode,
type: 'Directory'
}
);
if (opts.copyMode) {
promise = promise.then(copyMode.bind(copyMode, src, dst));

View File

@@ -10,41 +10,55 @@ function createLink(src, dst, type) {
var dstDir = path.dirname(dst);
// Create directory
return Q.nfcall(mkdirp, dstDir)
// Check if source exists
.then(function () {
return Q.nfcall(fs.stat, src)
.fail(function (error) {
if (error.code === 'ENOENT') {
throw createError('Failed to create link to ' + path.basename(src), 'ENOENT', {
details: src + ' does not exist or points to a non-existent file'
return (
Q.nfcall(mkdirp, dstDir)
// Check if source exists
.then(function() {
return Q.nfcall(fs.stat, src).fail(function(error) {
if (error.code === 'ENOENT') {
throw createError(
'Failed to create link to ' + path.basename(src),
'ENOENT',
{
details:
src +
' does not exist or points to a non-existent file'
}
);
}
throw error;
});
}
})
// Create symlink
.then(function(stat) {
type = type || (stat.isDirectory() ? 'dir' : 'file');
throw error;
});
})
// Create symlink
.then(function (stat) {
type = type || (stat.isDirectory() ? 'dir' : 'file');
return Q.nfcall(fs.symlink, src, dst, type).fail(function(err) {
if (!isWin || err.code !== 'EPERM') {
throw err;
}
return Q.nfcall(fs.symlink, src, dst, type)
.fail(function (err) {
if (!isWin || err.code !== 'EPERM') {
throw err;
}
// Try with type "junction" on Windows
// Junctions behave equally to true symlinks and can be created in
// non elevated terminal (well, not always..)
return Q.nfcall(fs.symlink, src, dst, 'junction')
.fail(function (err) {
throw createError('Unable to create link to ' + path.basename(src), err.code, {
details: err.message.trim() + '\n\nTry running this command in an elevated terminal (run as root/administrator).'
// Try with type "junction" on Windows
// Junctions behave equally to true symlinks and can be created in
// non elevated terminal (well, not always..)
return Q.nfcall(fs.symlink, src, dst, 'junction').fail(
function(err) {
throw createError(
'Unable to create link to ' +
path.basename(src),
err.code,
{
details:
err.message.trim() +
'\n\nTry running this command in an elevated terminal (run as root/administrator).'
}
);
}
);
});
});
});
});
})
);
}
module.exports = createLink;

View File

@@ -20,50 +20,53 @@ function download(url, file, options) {
var deferred = Q.defer();
var progressDelay = 8000;
options = mout.object.mixIn({
retries: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 35000,
randomize: true,
progressDelay: progressDelay,
gzip: true
}, options || {});
options = mout.object.mixIn(
{
retries: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 35000,
randomize: true,
progressDelay: progressDelay,
gzip: true
},
options || {}
);
// Retry on network errors
operation = retry.operation(options);
operation.attempt(function () {
operation.attempt(function() {
Q.fcall(fetch, url, file, options)
.then(function (response) {
deferred.resolve(response);
})
.progress(function (status) {
deferred.notify(status);
})
.fail(function (error) {
// Save timeout before retrying to report
var timeout = operation._timeouts[0];
.then(function(response) {
deferred.resolve(response);
})
.progress(function(status) {
deferred.notify(status);
})
.fail(function(error) {
// Save timeout before retrying to report
var timeout = operation._timeouts[0];
// Reject if error is not a network error
if (errorCodes.indexOf(error.code) === -1) {
return deferred.reject(error);
}
// Reject if error is not a network error
if (errorCodes.indexOf(error.code) === -1) {
return deferred.reject(error);
}
// Next attempt will start reporting download progress immediately
progressDelay = 0;
// Next attempt will start reporting download progress immediately
progressDelay = 0;
// This will schedule next retry or return false
if (operation.retry(error)) {
deferred.notify({
retry: true,
delay: timeout,
error: error
});
} else {
deferred.reject(error);
}
});
// This will schedule next retry or return false
if (operation.retry(error)) {
deferred.notify({
retry: true,
delay: timeout,
error: error
});
} else {
deferred.reject(error);
}
});
});
return deferred.promise;
@@ -75,68 +78,75 @@ function fetch(url, file, options) {
var contentLength;
var bytesDownloaded = 0;
var reject = function (error) {
var reject = function(error) {
deferred.reject(error);
};
var req = progress(request(url, options), {
delay: options.progressDelay
})
.on('response', function (response) {
contentLength = Number(response.headers['content-length']);
.on('response', function(response) {
contentLength = Number(response.headers['content-length']);
var status = response.statusCode;
var status = response.statusCode;
if (status < 200 || status >= 300) {
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
}
if (status < 200 || status >= 300) {
return deferred.reject(
createError('Status code of ' + status, 'EHTTP')
);
}
var writeStream = createWriteStream(file);
var errored = false;
var writeStream = createWriteStream(file);
var errored = false;
// Change error listener so it cleans up writeStream before exiting
req.removeListener('error', reject);
req.on('error', function (error) {
errored = true;
destroy(req);
destroy(writeStream);
// Wait for writeStream to cleanup after itself...
// TODO: Maybe there's a better way?
setTimeout(function () {
deferred.reject(error);
}, 50);
});
writeStream.on('finish', function () {
if (!errored) {
// Change error listener so it cleans up writeStream before exiting
req.removeListener('error', reject);
req.on('error', function(error) {
errored = true;
destroy(req);
deferred.resolve(response);
destroy(writeStream);
// Wait for writeStream to cleanup after itself...
// TODO: Maybe there's a better way?
setTimeout(function() {
deferred.reject(error);
}, 50);
});
writeStream.on('finish', function() {
if (!errored) {
destroy(req);
deferred.resolve(response);
}
});
req.pipe(writeStream);
})
.on('data', function(data) {
bytesDownloaded += data.length;
})
.on('progress', function(state) {
deferred.notify(state);
})
.on('error', reject)
.on('end', function() {
// Check if the whole file was downloaded
// In some unstable connections the ACK/FIN packet might be sent in the
// middle of the download
// See: https://github.com/joyent/node/issues/6143
if (contentLength && bytesDownloaded < contentLength) {
req.emit(
'error',
createError(
'Transfer closed with ' +
(contentLength - bytesDownloaded) +
' bytes remaining to read',
'EINCOMPLETE'
)
);
}
});
req.pipe(writeStream);
})
.on('data', function (data) {
bytesDownloaded += data.length;
})
.on('progress', function (state) {
deferred.notify(state);
})
.on('error', reject)
.on('end', function () {
// Check if the whole file was downloaded
// In some unstable connections the ACK/FIN packet might be sent in the
// middle of the download
// See: https://github.com/joyent/node/issues/6143
if (contentLength && bytesDownloaded < contentLength) {
req.emit('error', createError(
'Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read',
'EINCOMPLETE'
));
}
});
return deferred.promise;
}

View File

@@ -38,13 +38,13 @@ function extractZip(archive, dst) {
var deferred = Q.defer();
new DecompressZip(archive)
.on('error', deferred.reject)
.on('extract', deferred.resolve.bind(deferred, dst))
.extract({
path: dst,
follow: false, // Do not follow symlinks (#699)
filter: filterSymlinks // Filter symlink files
});
.on('error', deferred.reject)
.on('extract', deferred.resolve.bind(deferred, dst))
.extract({
path: dst,
follow: false, // Do not follow symlinks (#699)
filter: filterSymlinks // Filter symlink files
});
return deferred.promise;
}
@@ -54,22 +54,25 @@ function extractTar(archive, dst) {
var stream = fs.createReadStream(archive);
var reject = function (error) {
var reject = function(error) {
destroy(stream);
deferred.reject(error);
};
stream.on('error', reject)
.pipe(tar.extract(dst, {
ignore: isSymlink, // Filter symlink files
dmode: 0555, // Ensure dirs are readable
fmode: 0444 // Ensure files are readable
}))
.on('error', reject)
.on('finish', function (result) {
destroy(stream);
deferred.resolve(dst);
});
stream
.on('error', reject)
.pipe(
tar.extract(dst, {
ignore: isSymlink, // Filter symlink files
dmode: 0555, // Ensure dirs are readable
fmode: 0444 // Ensure files are readable
})
)
.on('error', reject)
.on('finish', function(result) {
destroy(stream);
deferred.resolve(dst);
});
return deferred.promise;
}
@@ -79,24 +82,27 @@ function extractTarGz(archive, dst) {
var stream = fs.createReadStream(archive);
var reject = function (error) {
var reject = function(error) {
destroy(stream);
deferred.reject(error);
};
stream.on('error', reject)
.pipe(zlib.createGunzip())
.on('error', reject)
.pipe(tar.extract(dst, {
ignore: isSymlink, // Filter symlink files
dmode: 0555, // Ensure dirs are readable
fmode: 0444 // Ensure files are readable
}))
.on('error', reject)
.on('finish', function (result) {
destroy(stream);
deferred.resolve(dst);
});
stream
.on('error', reject)
.pipe(zlib.createGunzip())
.on('error', reject)
.pipe(
tar.extract(dst, {
ignore: isSymlink, // Filter symlink files
dmode: 0555, // Ensure dirs are readable
fmode: 0444 // Ensure files are readable
})
)
.on('error', reject)
.on('finish', function(result) {
destroy(stream);
deferred.resolve(dst);
});
return deferred.promise;
}
@@ -106,25 +112,26 @@ function extractGz(archive, dst) {
var stream = fs.createReadStream(archive);
var reject = function (error) {
var reject = function(error) {
destroy(stream);
deferred.reject(error);
};
stream.on('error', reject)
.pipe(zlib.createGunzip())
.on('error', reject)
.pipe(createWriteStream(dst))
.on('error', reject)
.on('finish', function (result) {
destroy(stream);
deferred.resolve(dst);
});
stream
.on('error', reject)
.pipe(zlib.createGunzip())
.on('error', reject)
.pipe(createWriteStream(dst))
.on('error', reject)
.on('finish', function(result) {
destroy(stream);
deferred.resolve(dst);
});
return deferred.promise;
}
function isSymlink(entry) {
return entry.type === 'SymbolicLink';
function isSymlink(_, entry) {
return entry.type === 'symlink';
}
function filterSymlinks(entry) {
@@ -136,7 +143,7 @@ function getExtractor(archive) {
// This ensures that upper-cased extensions work
archive = archive.toLowerCase();
var type = mout.array.find(extractorTypes, function (type) {
var type = mout.array.find(extractorTypes, function(type) {
return mout.string.endsWith(archive, type);
});
@@ -144,8 +151,7 @@ function getExtractor(archive) {
}
function isSingleDir(dir) {
return Q.nfcall(fs.readdir, dir)
.then(function (files) {
return Q.nfcall(fs.readdir, dir).then(function(files) {
var singleDir;
// Remove any OS specific files from the files array
@@ -158,8 +164,7 @@ function isSingleDir(dir) {
singleDir = path.join(dir, files[0]);
return Q.nfcall(fs.stat, singleDir)
.then(function (stat) {
return Q.nfcall(fs.stat, singleDir).then(function(stat) {
return stat.isDirectory() ? singleDir : false;
});
});
@@ -167,19 +172,19 @@ function isSingleDir(dir) {
function moveDirectory(srcDir, destDir) {
return Q.nfcall(fs.readdir, srcDir)
.then(function (files) {
var promises = files.map(function (file) {
var src = path.join(srcDir, file);
var dst = path.join(destDir, file);
.then(function(files) {
var promises = files.map(function(file) {
var src = path.join(srcDir, file);
var dst = path.join(destDir, file);
return Q.nfcall(fs.rename, src, dst);
return Q.nfcall(fs.rename, src, dst);
});
return Q.all(promises);
})
.then(function() {
return Q.nfcall(fs.rmdir, srcDir);
});
return Q.all(promises);
})
.then(function () {
return Q.nfcall(fs.rmdir, srcDir);
});
}
// -----------------------------
@@ -209,57 +214,64 @@ function extract(src, dst, opts) {
// If extractor is null, then the archive type is unknown
if (!extractor) {
return Q.reject(createError('File ' + src + ' is not a known archive', 'ENOTARCHIVE'));
return Q.reject(
createError(
'File ' + src + ' is not a known archive',
'ENOTARCHIVE'
)
);
}
// Extract to a temporary directory in case of file name clashes
return Q.nfcall(tmp.dir, {
template: dst + '-XXXXXX',
mode: 0777 & ~process.umask()
}).then(function (tempDir) {
// nfcall may return multiple callback arguments as an array
return Array.isArray(tempDir) ? tempDir[0] : tempDir;
}).then(function (tempDir) {
// Check archive file size
promise = Q.nfcall(fs.stat, src)
.then(function (stat) {
if (stat.size <= 8) {
throw createError('File ' + src + ' is an invalid archive', 'ENOTARCHIVE');
}
// Extract archive
return extractor(src, tempDir);
});
// Remove archive
if (!opts.keepArchive) {
promise = promise
.then(function () {
return Q.nfcall(fs.unlink, src);
});
}
// Move contents from the temporary directory
// If the contents are a single directory (and we're not preserving structure),
// move its contents directly instead.
promise = promise
.then(function () {
return isSingleDir(tempDir);
})
.then(function(tempDir) {
// nfcall may return multiple callback arguments as an array
return Array.isArray(tempDir) ? tempDir[0] : tempDir;
})
.then(function (singleDir) {
if (singleDir && !opts.keepStructure) {
return moveDirectory(singleDir, dst);
} else {
return moveDirectory(tempDir, dst);
}
});
.then(function(tempDir) {
// Check archive file size
promise = Q.nfcall(fs.stat, src).then(function(stat) {
if (stat.size <= 8) {
throw createError(
'File ' + src + ' is an invalid archive',
'ENOTARCHIVE'
);
}
// Resolve promise to the dst dir
return promise.then(function () {
return dst;
// Extract archive
return extractor(src, tempDir);
});
// Remove archive
if (!opts.keepArchive) {
promise = promise.then(function() {
return Q.nfcall(fs.unlink, src);
});
}
// Move contents from the temporary directory
// If the contents are a single directory (and we're not preserving structure),
// move its contents directly instead.
promise = promise
.then(function() {
return isSingleDir(tempDir);
})
.then(function(singleDir) {
if (singleDir && !opts.keepStructure) {
return moveDirectory(singleDir, dst);
} else {
return moveDirectory(tempDir, dst);
}
});
// Resolve promise to the dst dir
return promise.then(function() {
return dst;
});
});
});
}
module.exports = extract;

View File

@@ -5,14 +5,14 @@ var readdirSync = fs.readdirSync.bind(fs);
module.exports = fs;
module.exports.readdir = function (dir, callback) {
fs.stat(dir, function (err, stats) {
module.exports.readdir = function(dir, callback) {
fs.stat(dir, function(err, stats) {
if (err) return callback(err);
if (stats.isDirectory()) {
return readdir(dir, callback);
} else {
var error = new Error('ENOTDIR, not a directory \'' + dir + '\'');
var error = new Error("ENOTDIR, not a directory '" + dir + "'");
error.code = 'ENOTDIR';
error.path = dir;
error.errono = -20;
@@ -21,7 +21,7 @@ module.exports.readdir = function (dir, callback) {
});
};
module.exports.readdirSync = function (dir) {
module.exports.readdirSync = function(dir) {
var stats = fs.statSync(dir);
if (stats.isDirectory()) {

View File

@@ -13,31 +13,43 @@ function readJson(file, options) {
options = options || {};
// Read
return Q.nfcall(bowerJson.read, file, options)
.spread(function (json, jsonFile) {
var deprecated;
return Q.nfcall(bowerJson.read, file, options).spread(
function(json, jsonFile) {
var deprecated;
jsonFile = path.basename(jsonFile);
deprecated = jsonFile === 'component.json' ? jsonFile : false;
if (options.logger) {
var issues = bowerJson.getIssues(json);
if (issues.warnings.length > 0) {
options.logger.warn('invalid-meta', 'for:' + jsonFile);
}
issues.warnings.forEach(function(warning) {
options.logger.warn('invalid-meta', warning);
});
}
return [json, deprecated, false];
}, function (err) {
// No json file was found, assume one
if (err.code === 'ENOENT' && options.assume) {
return [bowerJson.parse(options.assume, options), false, true];
jsonFile = path.basename(jsonFile);
deprecated = jsonFile === 'component.json' ? jsonFile : false;
return [json, deprecated, false];
},
function(err) {
// No json file was found, assume one
if (err.code === 'ENOENT' && options.assume) {
return [bowerJson.parse(options.assume, options), false, true];
}
err.details = err.message;
if (err.file) {
err.message = 'Failed to read ' + err.file;
err.data = { filename: err.file };
} else {
err.message = 'Failed to read json from ' + file;
}
throw err;
}
err.details = err.message;
if (err.file) {
err.message = 'Failed to read ' + err.file;
err.data = { filename: err.file };
} else {
err.message = 'Failed to read json from ' + file;
}
throw err;
});
);
}
module.exports = readJson;

View File

@@ -2,7 +2,7 @@ var path = require('path');
var isPathAbsolute = require('./isPathAbsolute');
function relativeToBaseDir(baseDir) {
return function (filePath) {
return function(filePath) {
if (isPathAbsolute(filePath)) {
return path.resolve(filePath);
} else {

View File

@@ -14,7 +14,7 @@ function removeIgnores(dir, meta) {
// Don't ignore main files
nonIgnored = nonIgnored.concat(meta.main || []);
nonIgnored = nonIgnored.map(function (file) {
nonIgnored = nonIgnored.map(function(file) {
return path.join(dir, file);
});
@@ -27,7 +27,7 @@ function removeIgnores(dir, meta) {
// Monkey patch applyIgnores such that we get hold of all ignored files
applyIgnores = reader.applyIgnores;
reader.applyIgnores = function (entry) {
reader.applyIgnores = function(entry) {
var ret = applyIgnores.apply(this, arguments);
if (!ret) {
@@ -38,28 +38,27 @@ function removeIgnores(dir, meta) {
};
reader
.on('child', function (entry) {
nonIgnored.push(entry.path);
})
.on('error', deferred.reject)
.on('end', function () {
var promises;
.on('child', function(entry) {
nonIgnored.push(entry.path);
})
.on('error', deferred.reject)
.on('end', function() {
var promises;
// Ensure that we are not ignoring files that should not be ignored!
ignored = mout.array.unique(ignored);
ignored = ignored.filter(function (file) {
return nonIgnored.indexOf(file) === -1;
// Ensure that we are not ignoring files that should not be ignored!
ignored = mout.array.unique(ignored);
ignored = ignored.filter(function(file) {
return nonIgnored.indexOf(file) === -1;
});
// Delete all the ignored files
promises = ignored.map(function(file) {
return Q.nfcall(rimraf, file);
});
return Q.all(promises).then(deferred.resolve, deferred.reject);
});
// Delete all the ignored files
promises = ignored.map(function (file) {
return Q.nfcall(rimraf, file);
});
return Q.all(promises)
.then(deferred.resolve, deferred.reject);
});
return deferred.promise;
}

View File

@@ -4,9 +4,9 @@ var resolve = require('resolve');
function startsWith(string, searchString, position) {
position = position || 0;
return string.substr(position, searchString.length) === searchString;
};
}
module.exports = function (id, options) {
module.exports = function(id, options) {
var resolvedPath;
var cwd = (options || {}).cwd || process.cwd();
@@ -19,4 +19,4 @@ module.exports = function (id, options) {
}
return resolvedPath;
}
};

View File

@@ -2,15 +2,15 @@ var rimraf = require('rimraf');
var chmodr = require('chmodr');
var fs = require('./fs');
module.exports = function (dir, callback) {
var checkAndRetry = function (e) {
fs.lstat(dir, function (err, stats) {
module.exports = function(dir, callback) {
var checkAndRetry = function(e) {
fs.lstat(dir, function(err, stats) {
if (err) {
if (err.code === 'ENOENT') return callback();
return callback(e);
}
chmodr(dir, 0777, function (err) {
chmodr(dir, 0777, function(err) {
if (err) return callback(e);
rimraf(dir, callback);
});
@@ -24,8 +24,8 @@ module.exports = function (dir, callback) {
}
};
module.exports.sync = function (dir) {
var checkAndRetry = function () {
module.exports.sync = function(dir) {
var checkAndRetry = function() {
try {
fs.lstatSync(dir);
chmodr.sync(dir, 0777);

View File

@@ -1,4 +1,3 @@
'use strict';
var isRoot = require('is-root');
var createError = require('./createError');
@@ -12,18 +11,23 @@ function rootCheck(options, config) {
return;
}
errorMsg = 'Since bower is a user command, there is no need to execute it with \
errorMsg =
'Since bower is a user command, there is no need to execute it with \
superuser permissions.\nIf you\'re having permission errors when using bower without \
sudo, please spend a few minutes learning more about how your system should work and \
make any necessary repairs.\n\n\
http://www.joyent.com/blog/installing-node-and-npm\n\
https://gist.github.com/isaacs/579814\n\n\
You can however run a command with sudo using --allow-root option';
You can however run a command with sudo using "--allow-root" option';
if (isRoot()) {
var cli = require('./cli');
renderer = cli.getRenderer('', false, config);
renderer.error(createError('Cannot be run with sudo', 'ESUDO', { details : errorMsg }));
renderer.error(
createError('Cannot be run with sudo', 'ESUDO', {
details: errorMsg
})
);
process.exit(1);
}
}

View File

@@ -6,13 +6,13 @@ function maxSatisfying(versions, range, strictMatch) {
var filteredVersions;
// Filter only valid versions, since semver.maxSatisfying() throws an error
versions = versions.filter(function (version) {
versions = versions.filter(function(version) {
return semver.valid(version);
});
// Exact version & range match
if (semver.valid(range)) {
version = mout.array.find(versions, function (version) {
version = mout.array.find(versions, function(version) {
return version === range;
});
if (version) {
@@ -25,7 +25,7 @@ function maxSatisfying(versions, range, strictMatch) {
// When strict match is enabled give priority to non-pre-releases
// We do this by filtering every pre-release version
if (strictMatch) {
filteredVersions = versions.map(function (version) {
filteredVersions = versions.map(function(version) {
return !isPreRelease(version) ? version : null;
});
@@ -57,7 +57,10 @@ function clean(version) {
}
// Keep builds!
return parsed.version + (parsed.build.length ? '+' + parsed.build.join('.') : '');
return (
parsed.version +
(parsed.build.length ? '+' + parsed.build.join('.') : '')
);
}
function isPreRelease(version) {

View File

@@ -8,7 +8,7 @@ var templatesDir = path.resolve(__dirname, '../templates');
var cache = {};
// Register helpers
mout.object.forOwn(helpers, function (register) {
mout.object.forOwn(helpers, function(register) {
register(Handlebars);
});

View File

@@ -1,3 +1,12 @@
var version = require('../version');
module.exports = 'node/' + process.version + ' ' + process.platform + ' ' + process.arch + ' ' + ';Bower ' + version;
module.exports =
'node/' +
process.version +
' ' +
process.platform +
' ' +
process.arch +
' ' +
';Bower ' +
version;

View File

@@ -5,20 +5,18 @@ function validLink(file) {
// Ensures that a file is a symlink that points
// to a valid file
return Q.nfcall(fs.lstat, file)
.then(function (lstat) {
if (!lstat.isSymbolicLink()) {
return [false];
}
.then(function(lstat) {
if (!lstat.isSymbolicLink()) {
return [false];
}
return Q.nfcall(fs.stat, file)
.then(function (stat) {
return [stat];
return Q.nfcall(fs.stat, file).then(function(stat) {
return [stat];
});
})
.fail(function(err) {
return [false, err];
});
})
.fail(function (err) {
return [false, err];
});
}
module.exports = validLink;

View File

@@ -1,6 +1,6 @@
{
"name": "bower",
"version": "1.7.8",
"version": "1.8.8",
"description": "The browser package manager",
"author": "Twitter",
"license": "MIT",
@@ -17,16 +17,16 @@
"dependencies": {
"abbrev": "^1.0.5",
"archy": "1.0.0",
"bower-config": "^1.3.1",
"bower-config": "^1.4.1",
"bower-endpoint-parser": "^0.2.2",
"bower-json": "^0.8.0",
"bower-json": "^0.8.1",
"bower-logger": "^0.2.2",
"bower-registry-client": "^1.0.0",
"cardinal": "0.4.4",
"chalk": "^1.0.0",
"chmodr": "^1.0.2",
"configstore": "^2.0.0",
"decompress-zip": "^0.2.1",
"decompress-zip": "^0.2.2",
"destroy": "^1.0.3",
"findup-sync": "^0.3.0",
"fs-write-stream-atomic": "1.0.8",
@@ -67,31 +67,35 @@
},
"devDependencies": {
"arr-diff": "^2.0.0",
"chai": "^1.10.0",
"coveralls": "^2.11.2",
"chai": "^3.5.0",
"coveralls": "^2.11.9",
"eslint": "^2.0.0",
"expect.js": "^0.3.1",
"grunt": "1.0.0-rc1",
"grunt-cli": "^1.1.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-eslint": "^18.0.0",
"grunt-exec": "sheerun/grunt-exec",
"grunt-simple-mocha": "^0.4.1",
"husky": "^0.14.3",
"in-publish": "^2.0.0",
"istanbul": "^0.3.5",
"load-grunt-tasks": "^2.0.0",
"mocha": "^2.1.0",
"istanbul": "^0.4.3",
"lint-staged": "^9.5.0",
"mocha": "^2.5.3",
"multiline": "^1.0.2",
"nock": "^3.1.0",
"node-uuid": "^1.4.2",
"proxyquire": "^1.3.0",
"spawn-sync": "1.0.13",
"nock": "^11.7.0",
"nock-legacy": "npm:nock@9.2.3",
"node-uuid": "^1.4.7",
"prettier": "^1.19.1",
"proxyquire": "^1.7.9",
"spawn-sync": "1.0.15",
"wrench": "^1.5.8"
},
"scripts": {
"test": "grunt test",
"ci": "grunt travis",
"coveralls": "coveralls",
"prepublish": "in-publish && echo 'You need to use \"grunt publish\" to publish bower' && false || not-in-publish"
"lint": "eslint .",
"test": "node test/packages.js && node test/packages-svn.js && mocha --timeout 15000 --reporter spec",
"prepublishOnly": "in-publish && echo 'You need to use \"node publish.js\" to publish bower' && false || not-in-publish",
"format": "prettier --write --single-quote --tab-width 4 '**/*.js'",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": [
"prettier --single-quote --tab-width 4"
]
},
"files": [
"bin",

View File

@@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

7
packages/bower-config/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
node_modules
npm-debug.log
test/assets/github-test-package
test/assets/github-test-package-copy
test/assets/temp
test/reports

View File

@@ -0,0 +1,103 @@
# Changelog
## 1.4.0
- Change default shorthand resolver from git:// to https://
## 1.3.1
- Ignore hook scripts for environment variable expansion
## 1.3.0 - 2015-12-07
- Allow the use of environment variables in .bowerrc. Fixes [#41](https://github.com/bower/config/issues/41)
- Loads the .bowerrc file from the cwd specified on the command line. Fixes [bower/bower#1993](https://github.com/bower/bower/issues/1993)
- Allwow for array notation in ENV variables [#44](https://github.com/bower/config/issues/44)
## 1.2.3 - 2015-11-27
- Restores env variables if they are undefined at the beginning
- Handles default setting for config.ca. Together with [bower/bower PR #1972](https://github.com/bower/bower/pull/1972), fixes downloading with `strict-ssl` using custom CA
- Displays an error message if .bowerrc is a directory instead of file. Fixes [bower/bower#2022](https://github.com/bower/bower/issues/2022)
## 1.2.2 - 2015-10-16
- Fixes registry configurartion expanding [bower/bower#1950](https://github.com/bower/bower/issues/1950)
## 1.2.1 - 2015-10-15
- Fixes case insenstivity HTTP_PROXY setting issue on Windows
## 1.2.0 - 2015-09-28
- Prevent defaulting cwd to process.cwd()
## 1.1.2 - 2015-09-27
- Performs only camel case normalisation before merging
## 1.1.1 - 2015-09-27
- Fix: Merge extra options after camel-case normalisation, instead of before it
## 1.1.0 - 2015-09-27
- Allow for overwriting options with .load(overwrites) / .read(cwd, overwrites)
## 1.0.1 - 2015-09-27
- Update dependencies and relax "mout" version range
- Most significant changes:
- graceful-fs updated from 2.x version to 4.x
- osenv updated to from 0.0.x to 0.1.x, [tmp location changed](https://github.com/npm/osenv/commit/d6eddbc026538b09026b1dbd60fbc081a8c67e03)
## 1.0.0 - 2015-09-27
- Support for no-proxy configuration variable
- Overwrite HTTP_PROXY, HTTPS_PROXY, and NO_PROXY env variables in load method
- Normalise paths to certificates with contents of them, [#28](https://github.com/bower/config/pull/28)
## 0.6.1 - 2015-04-1
- Fixes merging .bowerrc files upward directory tree. [#25](https://github.com/bower/config/issues/25)
## 0.6.0 - 2015-03-30
- Merge .bowerrc files upward directory tree (fixes [bower/bower#1689](https://github.com/bower/bower/issues/1689)) [#24](https://github.com/bower/config/pull/24)
- Allow NPM config variables (resolves [bower/bower#1711](https://github.com/bower/bower/issues/1711)) [#23](https://github.com/bower/config/pull/23)
## 0.5.2 - 2014-06-09
- Fixes downloading of bower modules with ignores when .bowerrc is overridden with a relative tmp path. [#17](https://github.com/bower/config/issues/17) [bower/bower#1299](https://github.com/bower/bower/issues/1299)
## 0.5.1 - 2014-05-21
- [perf] Uses the same mout version as bower
- [perf] Uses only relevant parts of mout. Related [bower/bower#1134](https://github.com/bower/bower/pull/1134)
## 0.5.0 - 2013-08-30
- Adds a DEFAULT_REGISTRY key to the Config class that exposes the bower registry UR. [#6](https://github.com/bower/config/issues/6)
## 0.4.5 - 2013-08-28
- Fixes crashing when home is not set
## 0.4.4 - 2013-08-21
- Supports nested environment variables [#8](https://github.com/bower/config/issues/8)
## 0.4.3 - 2013-08-19
- Improvement in argv.config parsing
## 0.4.2 - 2013-08-18
- Sets interative to auto
## 0.4.1 - 2013-08-18
- Generates a fake user instead of using 'unknown'
## 0.4.0 - 2013-08-16
- Suffixes temp folder with the user and 'bower'
## 0.3.5 - 2013-08-14
- Casts buffer to string
## 0.3.4 - 2013-08-11
- Empty .bowerrc files no longer throw an error.
## 0.3.3 - 2013-08-11
- Changes git folder to empty (was not being used anyway)
## 0.3.2 - 2013-08-07
- Uses a known user agent by default when a proxy.
## 0.3.1 - 2013-08-06
- Fixes Typo
## 0.3.0 - 2013-08-06
- Appends the username when using the temporary folder.

View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 Twitter and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,76 @@
# bower-config
> The Bower config (`.bowerrc`) reader and writer.
[Bower](http://bower.io/) can be configured using JSON in a `.bowerrc` file. For example:
{
"directory": "app/components/",
"timeout": 120000,
"registry": {
"search": [
"http://localhost:8000",
"https://registry.bower.io"
]
}
}
View the complete [.bowerrc specification](http://bower.io/docs/config/#bowerrc-specification) on the website for more details. Both the `bower.json` and `.bowerrc` specifications are maintained at [github.com/bower/spec](https://github.com/bower/spec).
## Install
```sh
$ npm install --save bower-config
```
## Usage
#### .load(overwrites)
Loads the bower configuration from the configuration files.
Configuration is overwritten (after camelcase normalisation) with `overwrites` argument.
This method overwrites following environment variables:
- `HTTP_PROXY` with `proxy` configuration variable
- `HTTPS_PROXY` with `https-proxy` configuration variable
- `NO_PROXY` with `no-proxy` configuration variable
It also clears `http_proxy`, `https_proxy`, and `no_proxy` environment variables.
To restore those variables you can use `restore` method.
#### restore()
Restores environment variables overwritten by `.load` method.
#### .toObject()
Returns a deep copy of the underlying configuration object.
The returned configuration is normalised.
The object keys will be camelCase.
#### #create(cwd)
Obtains a instance where `cwd` is the current working directory (defaults to `process.cwd`);
```js
var config = require('bower-config').create();
// You can also specify a working directory
var config2 = require('bower-config').create('./some/path');
```
#### #read(cwd, overrides)
Alias for:
```js
var configObject = (new Config(cwd)).load(overrides).toJson();
```
## License
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).

View File

@@ -0,0 +1,113 @@
var lang = require('mout/lang');
var object = require('mout/object');
var rc = require('./util/rc');
var expand = require('./util/expand');
var EnvProxy = require('./util/proxy');
var path = require('path');
var fs = require('fs');
function Config(cwd) {
this._cwd = cwd;
this._proxy = new EnvProxy();
this._config = {};
}
Config.prototype.load = function(overwrites) {
this._config = rc('bower', this._cwd);
this._config = object.merge(
expand(this._config || {}),
expand(overwrites || {})
);
this._config = normalise(this._config);
this._proxy.set(this._config);
return this;
};
Config.prototype.restore = function() {
this._proxy.restore();
};
function readCertFile(path) {
path = path || '';
var sep = '-----END CERTIFICATE-----';
var certificates;
if (path.indexOf(sep) === -1) {
certificates = fs.readFileSync(path, { encoding: 'utf8' });
} else {
certificates = path;
}
return certificates
.split(sep)
.filter(function(s) {
return !s.match(/^\s*$/);
})
.map(function(s) {
return s + sep;
});
}
function loadCAs(caConfig) {
// If a ca file path has been specified, expand that here to the file's
// contents. As a user can specify these individually, we must load them
// one by one.
for (var p in caConfig) {
if (caConfig.hasOwnProperty(p)) {
var prop = caConfig[p];
if (Array.isArray(prop)) {
caConfig[p] = prop.map(function(s) {
return readCertFile(s);
});
} else if (prop) {
caConfig[p] = readCertFile(prop);
}
}
}
}
Config.prototype.toObject = function() {
return lang.deepClone(this._config);
};
Config.create = function(cwd) {
return new Config(cwd);
};
Config.read = function(cwd, overrides) {
var config = Config.create(cwd);
return config.load(overrides).toObject();
};
function normalise(config) {
config = expand(config);
// Some backwards compatible things..
if (config.shorthandResolver) {
config.shorthandResolver = config.shorthandResolver
.replace(/\{\{\{/g, '{{')
.replace(/\}\}\}/g, '}}');
}
// Ensure that every registry endpoint does not end with /
config.registry.search = config.registry.search.map(function(url) {
return url.replace(/\/+$/, '');
});
config.registry.register = config.registry.register.replace(/\/+$/, '');
config.registry.publish = config.registry.publish.replace(/\/+$/, '');
config.tmp = path.resolve(config.tmp);
loadCAs(config.ca);
return config;
}
Config.DEFAULT_REGISTRY = require('./util/defaults').registry;
module.exports = Config;

View File

@@ -0,0 +1,46 @@
var path = require('path');
var paths = require('./paths');
// Guess proxy defined in the env
var proxy = process.env.HTTP_PROXY || process.env.http_proxy || null;
var httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy || proxy;
var noProxy = process.env.NO_PROXY || process.env.no_proxy;
// Use a well known user agent (in this case, curl) when using a proxy,
// to avoid potential filtering on many corporate proxies with blank or unknown agents
var userAgent =
!proxy && !httpsProxy
? 'node/' +
process.version +
' ' +
process.platform +
' ' +
process.arch
: 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5';
var defaults = {
directory: 'bower_components',
registry: 'https://registry.bower.io',
'shorthand-resolver': 'https://github.com/{{owner}}/{{package}}.git',
tmp: paths.tmp,
proxy: proxy,
'https-proxy': httpsProxy,
'no-proxy': noProxy,
timeout: 30000,
ca: { search: [] },
'strict-ssl': true,
'user-agent': userAgent,
color: true,
interactive: null,
storage: {
packages: path.join(paths.cache, 'packages'),
links: path.join(paths.data, 'links'),
completion: path.join(paths.data, 'completion'),
registry: path.join(paths.cache, 'registry'),
empty: path.join(paths.data, 'empty') // Empty dir, used in GIT_TEMPLATE_DIR among others
}
};
module.exports = defaults;

View File

@@ -0,0 +1,130 @@
var object = require('mout/object');
var lang = require('mout/lang');
var string = require('mout/string');
function camelCase(config) {
var camelCased = {};
// Camel case
object.forOwn(config, function(value, key) {
// Ignore null values
if (value == null) {
return;
}
key = string.camelCase(key.replace(/_/g, '-'));
camelCased[key] = lang.isPlainObject(value) ? camelCase(value) : value;
});
return camelCased;
}
// Function to replace ${VAR} - style variables
// with values set in the environment
// This function expects to be passed a string
function doEnvReplaceStr(f) {
// Un-tildify
var untildify = require('untildify');
f = untildify(f);
// replace any ${ENV} values with the appropriate environ.
var envExpr = /(\\*)\$\{([^}]+)\}/g;
return f.replace(envExpr, function(orig, esc, name) {
esc = esc.length && esc.length % 2;
if (esc) return orig;
if (undefined === process.env[name]) {
throw new Error(
'Environment variable used in .bowerrc is not defined: ' + orig
);
}
return process.env[name];
});
}
function envReplace(config) {
var envReplaced = {};
if (lang.isArray(config)) {
envReplaced = [];
}
object.forOwn(config, function(value, key) {
// Ignore null values
if (value == null) {
return;
}
// Ignore 'scripts'
// These hooks run within the shell
// environment variable expansion is not required
if (key === 'scripts' && lang.isPlainObject(value)) {
envReplaced[key] = value;
return;
}
// Perform variable replacements based on var type
if (lang.isPlainObject(value)) {
envReplaced[key] = envReplace(value);
} else if (lang.isArray(value)) {
envReplaced[key] = envReplace(value);
} else if (lang.isString(value)) {
envReplaced[key] = doEnvReplaceStr(value);
} else {
envReplaced[key] = value;
}
});
return envReplaced;
}
function expand(config) {
config = camelCase(config);
config = envReplace(config);
if (typeof config.registry === 'string') {
config.registry = {
default: config.registry,
search: [config.registry],
register: config.registry,
publish: config.registry
};
} else if (typeof config.registry === 'object') {
config.registry.default =
config.registry.default || 'https://registry.bower.io';
config.registry = {
default: config.registry.default,
search: config.registry.search || config.registry.default,
register: config.registry.register || config.registry.default,
publish: config.registry.publish || config.registry.default
};
if (config.registry.search && !Array.isArray(config.registry.search)) {
config.registry.search = [config.registry.search];
}
}
// CA
if (typeof config.ca === 'string') {
config.ca = {
default: config.ca,
search: [config.ca],
register: config.ca,
publish: config.ca
};
} else if (typeof config.ca === 'object') {
if (config.ca.search && !Array.isArray(config.ca.search)) {
config.ca.search = [config.ca.search];
}
if (config.ca.default) {
config.ca.search = config.ca.search || config.ca.default;
config.ca.register = config.ca.register || config.ca.default;
config.ca.publish = config.ca.publish || config.ca.default;
}
}
return config;
}
module.exports = expand;

View File

@@ -0,0 +1,52 @@
var os = require('os');
var path = require('path');
var osenv = require('osenv');
var crypto = require('crypto');
function generateFakeUser() {
var uid =
process.pid +
'-' +
Date.now() +
'-' +
Math.floor(Math.random() * 1000000);
return crypto
.createHash('md5')
.update(uid)
.digest('hex');
}
// Assume XDG defaults
// See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
var paths = {
config: process.env.XDG_CONFIG_HOME,
data: process.env.XDG_DATA_HOME,
cache: process.env.XDG_CACHE_HOME
};
// Guess some needed properties based on the user OS
var user = (osenv.user() || generateFakeUser()).replace(/\\/g, '-');
var tmp = path.join(os.tmpdir ? os.tmpdir() : os.tmpDir(), user);
var home = osenv.home();
var base;
// Fallbacks for windows
if (process.platform === 'win32') {
base = path.resolve(process.env.LOCALAPPDATA || home || tmp);
base = path.join(base, 'bower');
paths.config = paths.config || path.join(base, 'config');
paths.data = paths.data || path.join(base, 'data');
paths.cache = paths.cache || path.join(base, 'cache');
// Fallbacks for other operating systems
} else {
base = path.resolve(home || tmp);
paths.config = paths.config || path.join(base, '.config/bower');
paths.data = paths.data || path.join(base, '.local/share/bower');
paths.cache = paths.cache || path.join(base, '.cache/bower');
}
paths.tmp = path.resolve(path.join(tmp, 'bower'));
module.exports = paths;

View File

@@ -0,0 +1,79 @@
// EnvProxy uses the proxy vaiables passed to it in set and sets the
// process.env uppercase proxy variables to them with the ability
// to restore the original values later
var EnvProxy = function() {
this.restoreFrom = {};
};
EnvProxy.prototype.set = function(config) {
this.config = config;
// Override environment defaults if proxy config options are set
// This will make requests.js follow the proxies in config
if (Object.prototype.hasOwnProperty.call(config, 'noProxy')) {
this.restoreFrom.NO_PROXY = process.env.NO_PROXY;
this.restoreFrom.no_proxy = process.env.no_proxy;
delete process.env.no_proxy;
process.env.NO_PROXY = config.noProxy;
}
if (Object.prototype.hasOwnProperty.call(config, 'proxy')) {
this.restoreFrom.HTTP_PROXY = process.env.HTTP_PROXY;
this.restoreFrom.http_proxy = process.env.http_proxy;
delete process.env.http_proxy;
process.env.HTTP_PROXY = config.proxy;
}
if (Object.prototype.hasOwnProperty.call(config, 'httpsProxy')) {
this.restoreFrom.HTTPS_PROXY = process.env.HTTPS_PROXY;
this.restoreFrom.https_proxy = process.env.https_proxy;
delete process.env.https_proxy;
process.env.HTTPS_PROXY = config.httpsProxy;
}
};
EnvProxy.prototype.restore = function() {
if (Object.prototype.hasOwnProperty.call(this.config, 'noProxy')) {
if (this.restoreFrom.NO_PROXY !== undefined) {
process.env.NO_PROXY = this.restoreFrom.NO_PROXY;
} else {
delete process.env.NO_PROXY;
}
if (this.restoreFrom.no_proxy !== undefined) {
process.env.no_proxy = this.restoreFrom.no_proxy;
} else {
delete process.env.no_proxy;
}
}
if (Object.prototype.hasOwnProperty.call(this.config, 'proxy')) {
if (this.restoreFrom.HTTP_PROXY !== undefined) {
process.env.HTTP_PROXY = this.restoreFrom.HTTP_PROXY;
} else {
delete process.env.HTTP_PROXY;
}
if (this.restoreFrom.http_proxy !== undefined) {
process.env.http_proxy = this.restoreFrom.http_proxy;
} else {
delete process.env.http_proxy;
}
}
if (Object.prototype.hasOwnProperty.call(this.config, 'httpsProxy')) {
if (this.restoreFrom.HTTPS_PROXY !== undefined) {
process.env.HTTPS_PROXY = this.restoreFrom.HTTPS_PROXY;
} else {
delete process.env.HTTPS_PROXY;
}
if (this.restoreFrom.https_proxy !== undefined) {
process.env.https_proxy = this.restoreFrom.https_proxy;
} else {
delete process.env.https_proxy;
}
}
};
module.exports = EnvProxy;

View File

@@ -0,0 +1,157 @@
var path = require('path');
var fs = require('graceful-fs');
var optimist = require('optimist');
var osenv = require('osenv');
var object = require('mout/object');
var string = require('mout/string');
var paths = require('./paths');
var defaults = require('./defaults');
var win = process.platform === 'win32';
var home = osenv.home();
function rc(name, cwd, argv) {
var argvConfig;
argv = argv || optimist.argv;
// Parse --config.foo=false
argvConfig = object.map(argv.config || {}, function(value) {
return value === 'false' ? false : value;
});
// If we have specified a cwd then use this as the base for getting config.
cwd = argvConfig.cwd ? argvConfig.cwd : cwd;
if (cwd) {
return object.deepMixIn.apply(null, [
{},
defaults,
{ cwd: cwd },
win ? {} : json(path.join('/etc', name + 'rc')),
!home ? {} : json(path.join(home, '.' + name + 'rc')),
json(path.join(paths.config, name + 'rc')),
json(find('.' + name + 'rc', cwd)),
env('npm_package_config_' + name + '_'),
env(name + '_'),
argvConfig
]);
} else {
return object.deepMixIn.apply(null, [
{},
defaults,
win ? {} : json(path.join('/etc', name + 'rc')),
!home ? {} : json(path.join(home, '.' + name + 'rc')),
json(path.join(paths.config, name + 'rc')),
env('npm_package_config_' + name + '_'),
env(name + '_'),
argvConfig
]);
}
}
function parse(content, file) {
var error;
if (!content.trim().length) {
return {};
}
try {
return JSON.parse(content);
} catch (e) {
if (file) {
error = new Error('Unable to parse ' + file + ': ' + e.message);
} else {
error = new Error('Unable to parse rc config: ' + e.message);
}
error.details = content;
error.code = 'EMALFORMED';
throw error;
}
}
function json(file) {
var content = {};
if (!Array.isArray(file)) {
try {
content = fs.readFileSync(file).toString();
} catch (err) {
return null;
}
return parse(content, file);
} else {
// This is multiple json files
file.forEach(function(filename) {
if (fs.statSync(filename).isDirectory()) {
var error;
error = new Error(filename + ' should not be a directory');
error.code = 'EFILEISDIR';
throw error;
}
var json = fs.readFileSync(filename).toString();
json = parse(json, filename);
content = object.merge(content, json);
});
return content;
}
}
function env(prefix) {
var obj = {};
var prefixLength = prefix.length;
prefix = prefix.toLowerCase();
object.forOwn(process.env, function(value, key) {
key = key.toLowerCase();
if (string.startsWith(key, prefix)) {
var parsedKey = key
.substr(prefixLength)
.replace(/__/g, '.') // __ is used for nesting
.replace(/_/g, '-'); // _ is used as a - separator
//use a convention patern to accept array from process.env
//e.g. export bower_registry__search='["http://abc.com","http://def.com"]'
var match = /\[([^\]]*)\]/g.exec(value);
var targetValue;
if (!match || match.length === 0) {
targetValue = value;
} else {
targetValue = match[1].split(',').map(function(m) {
return m.trim();
});
}
object.set(obj, parsedKey, targetValue);
}
});
return obj;
}
function find(filename, dir) {
var files = [];
var walk = function(filename, dir) {
var file = path.join(dir, filename);
var parent = path.dirname(dir);
if (fs.existsSync(file)) {
files.push(file);
}
if (parent !== dir) {
walk(filename, parent);
}
};
walk(filename, dir);
files.reverse();
return files;
}
module.exports = rc;

View File

@@ -0,0 +1,35 @@
{
"name": "bower-config",
"version": "1.4.0",
"description": "The Bower config reader and writer.",
"author": "Twitter",
"license": "MIT",
"repository": "https://github.com/bower/bower/tree/master/packages/bower-config",
"main": "lib/Config",
"homepage": "http://bower.io",
"engines": {
"node": ">=0.8.0"
},
"dependencies": {
"graceful-fs": "^4.1.3",
"mout": "^1.0.0",
"optimist": "^0.6.1",
"osenv": "^0.1.3",
"untildify": "^2.1.0"
},
"devDependencies": {
"expect.js": "^0.3.1",
"glob": "^4.5.3",
"mkdirp": "^0.5.0",
"mocha": "^2.5.3",
"node-uuid": "^1.4.3",
"q": "^1.2.0",
"rimraf": "^2.3.2"
},
"scripts": {
"test": "mocha test"
},
"files": [
"lib"
]
}

View File

@@ -0,0 +1,3 @@
{
"ca": "Equifax Secure CA\n=================\n-----BEGIN CERTIFICATE-----\nMIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE\nChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5\nMB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT\nB0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB\nnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR\nfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW\n8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG\nA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE\nCxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG\nA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS\nspXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB\nAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961\nzgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB\nBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95\n70+sB3c4\n-----END CERTIFICATE-----\n\nGlobalSign Root CA\n==================\n-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\nGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\nb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\nBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\nVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\nDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\nTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\nKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\nc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\ngzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\nAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\nY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\nj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\nhm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\nX4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n-----END CERTIFICATE-----"
}

View File

@@ -0,0 +1,3 @@
{
"ca": "test/assets/custom-ca/ca-bundle.crt"
}

View File

@@ -0,0 +1,40 @@
Equifax Secure CA
=================
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
70+sB3c4
-----END CERTIFICATE-----
GlobalSign Root CA
==================
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,15 @@
{
"scripts" : {
"postinstall" : "${_myshellvar}"
},
"storage" : {
"packages" : "${_BOWERRC_MY_PACKAGES}",
"registry" : {
"register": "~/.bower-test/registry",
"search": [
"${_BOWERRC_MY_USER}:${_BOWERRC_MY_PASS}"
]
}
},
"tmp" : "${_BOWERRC_MY_TMP}"
}

View File

@@ -0,0 +1,5 @@
{
"proxy": "http://HTTP_PROXY",
"https-proxy": "http://HTTPS_PROXY",
"no-proxy": "google.com"
}

View File

@@ -0,0 +1,131 @@
var Q = require('q');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var uuid = require('node-uuid');
var object = require('mout/object');
var fs = require('fs');
var glob = require('glob');
var os = require('os');
var path = require('path');
// For better promise errors
Q.longStackSupport = true;
var tmpLocation = path.join(
os.tmpdir ? os.tmpdir() : os.tmpDir(),
'bower-config-tests',
uuid.v4().slice(0, 8)
);
after(function() {
rimraf.sync(tmpLocation);
});
exports.TempDir = (function() {
function TempDir(defaults) {
this.path = path.join(tmpLocation, uuid.v4());
this.defaults = defaults;
}
TempDir.prototype.create = function(files, defaults) {
var that = this;
defaults = defaults || this.defaults || {};
files = object.merge(files || {}, defaults);
this.meta = function(tag) {
if (tag) {
return files[tag]['bower.json'];
} else {
return files['bower.json'];
}
};
if (files) {
object.forOwn(files, function(contents, filepath) {
if (typeof contents === 'object') {
contents = JSON.stringify(contents, null, ' ') + '\n';
}
var fullPath = path.join(that.path, filepath);
mkdirp.sync(path.dirname(fullPath));
fs.writeFileSync(fullPath, contents);
});
}
return this;
};
TempDir.prototype.prepare = function(files) {
rimraf.sync(this.path);
mkdirp.sync(this.path);
this.create(files);
return this;
};
// TODO: Rewrite to synchronous form
TempDir.prototype.prepareGit = function(revisions) {
var that = this;
revisions = object.merge(revisions || {}, this.defaults);
rimraf.sync(that.path);
mkdirp.sync(that.path);
var promise = new Q();
object.forOwn(revisions, function(files, tag) {
promise = promise
.then(function() {
return that.git('init');
})
.then(function() {
that.glob('./!(.git)').map(function(removePath) {
var fullPath = path.join(that.path, removePath);
rimraf.sync(fullPath);
});
that.create(files, {});
})
.then(function() {
return that.git('add', '-A');
})
.then(function() {
return that.git('commit', '-m"commit"');
})
.then(function() {
return that.git('tag', tag);
});
});
return promise;
};
TempDir.prototype.glob = function(pattern) {
return glob.sync(pattern, {
cwd: this.path,
dot: true
});
};
TempDir.prototype.getPath = function(name) {
return path.join(this.path, name);
};
TempDir.prototype.read = function(name) {
return fs.readFileSync(this.getPath(name), 'utf8');
};
TempDir.prototype.readJson = function(name) {
return JSON.parse(this.read(name));
};
TempDir.prototype.exists = function(name) {
return fs.existsSync(path.join(this.path, name));
};
return TempDir;
})();

Some files were not shown because too many files have changed in this diff Show More