Compare commits

...

183 Commits

Author SHA1 Message Date
Adam Stankiewicz
e97bf479fb Merge pull request #1527 from LaurentGoderre/v1.3.11
Bump and update changelog
2014-09-18 02:26:50 +02:00
Laurent Goderre
fe2f71c9b8 Bump and update changelog 2014-09-17 11:12:17 -04:00
Adam Stankiewicz
ee6c483dd4 Merge pull request #1519 from LaurentGoderre/fix-1518
Fix update command not installing missing packages
2014-09-16 22:39:49 +02:00
Laurent Goderre
e83ab86f1f Added update test cases 2014-09-16 16:23:38 -04:00
Laurent Goderre
ef237fc521 Added the missing install command to the update task
Fixes #1518
2014-09-16 16:23:25 -04:00
Adam Stankiewicz
fd4d68038b Bump and update changelog 2014-09-13 16:39:35 +02:00
Adam Stankiewicz
8e458c21e9 [test] Make sure install ends before running next test 2014-09-13 16:12:06 +02:00
Adam Stankiewicz
4ab36cb3bc [test] Use new temp location per process 2014-09-13 16:04:02 +02:00
Adam Stankiewicz
2d149f3a09 Merge pull request #1507 from bower/fix/skip-prompt
Fix/skip prompt
2014-09-13 01:25:20 +02:00
Adam Stankiewicz
dfd2c7a3d2 Add tests to analytics and fix them 2014-09-12 21:28:44 +02:00
John Schulz
6637762aec "analytics" value in \.bowerrc\ removes need to prompt user 2014-09-12 00:26:41 +02:00
Laurent Goderre
615eba3b40 Update p-throttle to allow displaying the output of hooks
Fixes #1484
2014-09-12 00:04:19 +02:00
Adam Stankiewicz
dc183125fc [test] Show coverage status for master branch 2014-09-08 03:12:36 +02:00
Adam Stankiewicz
c3b69d1201 [test] Provide git credentials for tests on CI 2014-09-08 03:10:02 +02:00
Adam Stankiewicz
3e1a50ab9a [test] Add tests for git repository install 2014-09-08 03:02:57 +02:00
Adam Stankiewicz
7565c71eb2 Warn about unnecessary resolution, #1061 2014-09-08 01:13:55 +02:00
Adam Stankiewicz
d743352bc0 Disable removing unnecessary resolutions, #1061 2014-09-08 01:02:31 +02:00
Sindre Sorhus
75ca72e3a8 Merge pull request #1508 from bower/fix/head-pipe
Prevent error when piping bower output to head, fixes #1396
2014-09-08 00:56:48 +02:00
Adam Stankiewicz
59d26c6825 Prevent error when piping bower output to head, fixes #1396 2014-09-07 23:46:20 +02:00
Adam Stankiewicz
5eed363e10 Merge pull request #1393 from Lukeas14/disable-shallow-clone-except-github
Disable shallow clones for all repos except those from github.com
2014-09-07 23:29:24 +02:00
Sindre Sorhus
49b2fdbde9 Merge pull request #1504 from bower/fix/hook-order
Run posthook after saving bower.json, fixes #1471
2014-09-07 20:06:28 +02:00
Burak Yigit Kaya
623f6e9542 Use tar-fs instead of tar for faster TAR extraction 2014-09-07 19:29:37 +02:00
Adam Stankiewicz
32356f23d3 Run posthook after saving bower.json, fixes #1471 2014-09-07 19:14:30 +02:00
Sindre Sorhus
321ddabfd5 Merge pull request #1502 from bower/fix/bowerrc-cwd
Read .bowerrc from specified cwd, fixes #1301
2014-09-06 18:15:55 +02:00
Sindre Sorhus
bf93a6a1ab make bower list --paths a bit prettier 2014-09-06 17:58:47 +02:00
Sindre Sorhus
91aa5dc6dd bump dependencies 2014-09-06 17:20:32 +02:00
Sindre Sorhus
56ed46b99a Merge pull request #1503 from bower/fix/windows-local-caching
Disable caching for windows absolute path, fixes #1416
2014-09-06 17:08:47 +02:00
Adam Stankiewicz
daaf21a4fe Merge pull request #1495 from mitogh/link-issues
Update the references to the issues and pulls in the CHANGELOG.md file
2014-09-06 14:40:55 +02:00
Adam Stankiewicz
06a8f2afab Read .bowerrc from specified cwd, fixes #1301 2014-09-06 02:38:40 +02:00
Adam Stankiewicz
cc04530c4a Merge pull request #1495 from mitogh/link-issues
Update the references to the issues and pulls in the CHANGELOG.md file
2014-09-04 23:14:21 +02:00
Crisoforo Gaspar Hernandez
0b6f62977a Update the references to the issues and pulls in the CHANGELOG.md file 2014-09-04 14:18:00 -05:00
Ben Schwarz
2a2996c22b Merge pull request #1483 from benschwarz/concurrency-reduction
Back concurrency down to 5 (from 50!)
2014-08-28 22:02:07 +10:00
Ben Schwarz
254aba0995 Back concurrency down to 5 (from 50!) 2014-08-28 20:28:22 +10:00
Ben Schwarz
23a81b3121 Merge pull request #1478 from fsmanuel/expose-version
expose bower version
2014-08-20 08:01:56 -07:00
Manuel Wiedenmann
c4659f816f expose bower version 2014-08-20 14:26:42 +02:00
Ben Schwarz
d1427e7d2e Merge pull request #1473 from bower/docs_update
Further simplify readme.md
2014-08-16 09:47:17 +10:00
Ray Shan
5584d1062e Further simplify readme.md 2014-08-15 16:26:23 -07:00
Sindre Sorhus
8cb41fe5fb Merge pull request #1464 from thorn0/patch-1
Mention updates in the main help
2014-08-11 18:35:42 +02:00
thorn0
cd893fec15 Mention updates in the main help
See #955. The same was done for `bower list -h`, but not for `bower help` AKA just `bower`.
2014-08-11 17:07:07 +03:00
Sindre Sorhus
2a01f178da Update changelog 2014-08-06 21:41:46 +02:00
Sindre Sorhus
a069d1e07d 1.3.9 2014-08-06 21:39:31 +02:00
Sindre Sorhus
76fa7f5200 Merge pull request #1434 from pornel/master
Fix "TypeError: Arguments to path.join must be strings" in GitHubResolver
2014-08-06 21:35:41 +02:00
Kornel Lesiński
7a0a86d51c When tmp.js returns cleanup callback along with the tmp dir path, nfcall changes return type to an array 2014-08-06 14:28:04 +01:00
Sindre Sorhus
8e283e43db Merge pull request #1442 from luckysw/master
Added .zip MIME type (e.g. the default served by Windows/IIS)
2014-07-30 08:38:23 -07:00
MadLux
794744d5a3 Added .zip MIME type (e.g. the default served by Windows/IIS) 2014-07-30 11:41:21 +02:00
Sindre Sorhus
5c3e69b045 Merge pull request #1436 from rayshan/bump_insight
analytics - bump insight, supports OS/node/CLI version tracking
2014-07-28 04:16:02 -07:00
Ray Shan
9cbd595cfd analytics - bump insight to 0.4.1, supports OS/node/CLI tool version tracking 2014-07-27 21:42:15 -07:00
Adam Stankiewicz
a5074eca7d [doc] Encourage to always use --save option 2014-07-15 14:11:29 +02:00
Sindre Sorhus
9386b117c1 Merge pull request #1415 from bower/simple-readme
Remove content that can be now be found on bower.io
2014-07-15 01:58:10 +02:00
Sindre Sorhus
7738248230 lock down 0.0.x versions
as `~` causes unexpected and non-semver behaviour on them.

https://github.com/isaacs/node-semver
2014-07-15 01:50:32 +02:00
David DeSandro
85f1f808d3 Remove content that can be now be found on bower.io
+ bower.json
+ custom install directory
+ bower search
+ using bower cache
+ .bowerrc
+ continuous integration server
+ interactive configuration
+ Defining a package
+ maintaining deps
+ consuming a package
+ programmatic api
+ assistance & contribution list

Ref #1371
2014-07-14 17:45:24 -04:00
Ben Schwarz
6dbafa22bb Merge pull request #1412 from chibicode/master
Fix typo on help-uninstall.json
2014-07-13 19:17:16 +10:00
Shu Uesugi
cadcd37681 Fix typo on help-uninstall.json 2014-07-13 16:36:41 +09:00
Paul Irish
a45ea6e1d8 Update changelog 2014-07-11 17:02:35 -07:00
Paul Irish
187f24de81 Bump to 1.3.8 2014-07-11 17:02:35 -07:00
Paul Irish
8df1f48226 Merge pull request #1407 from vladikoff/tmp-dep-fix
Fixes node-tmp update issue
2014-07-11 13:44:04 -07:00
vladikoff
7dbf332a94 Fixes node-tmp update issue 2014-07-11 13:40:58 -07:00
Sindre Sorhus
c09ddf6de1 Merge pull request #1395 from steveyken/master
Fixed typo
2014-07-09 23:48:49 +02:00
Sindre Sorhus
9b5fa99d6a bump update-notifier and simplify 2014-07-09 16:15:47 +02:00
Steve Kenworthy
b9abf32007 Fixed typo 2014-07-09 10:50:43 +08:00
Lucas, Justin
09ecb80625 Disable shallow clones for all repos except github.com 2014-07-08 11:39:14 -07:00
Sindre Sorhus
c4e9a0e340 bump deps 2014-07-05 00:26:46 +02:00
Adam Stankiewicz
b6cf4e1826 Update changelog 2014-07-04 13:02:12 +01:00
Adam Stankiewicz
ecb1619399 Bump to 1.3.7 2014-07-04 12:58:03 +01:00
Vladimir Alaev
83f4b7b699 Fix callstack error when processing installed packages with circular dependencies 2014-07-04 12:53:50 +01:00
insanehong
9b81ddf4d5 It should shows an error when bower install failed, fixes #922
- reason no bower.json in current directory
2014-07-03 15:10:38 +01:00
insanehong
4af17f0e40 Fixed bower list --paths` fails with TypeError, fixes #1383 2014-07-03 15:00:53 +01:00
Adam Stankiewicz
2fbc036e69 Update the changelog 2014-07-02 14:04:01 +01:00
Adam Stankiewicz
d236a12b8f Bump version to 1.3.6 2014-07-02 12:28:48 +01:00
Adam Stankiewicz
232be333ad Add tests for removeIgnores utility 2014-07-02 11:37:56 +01:00
Tim Monks
192e5af797 Add integration tests for bower commands 2014-07-02 11:16:12 +01:00
Tim Monks
deb39b8f34 Publish to coveralls.io from travis 2014-07-02 11:16:10 +01:00
Adam Stankiewicz
197b41d97a Merge pull request #1366 from sheerun/fix/local
[#1356] Disable caching for local resources
2014-07-02 10:31:30 +01:00
Adam Stankiewicz
7b11a57c6f Merge pull request #1365 from sheerun/fix/force
[#931] Make --force always re-run installation
2014-07-02 10:26:50 +01:00
Adam Stankiewicz
eac7945fae Merge pull request #1372 from incompl/master
Add --version to --help
2014-06-27 23:03:35 +01:00
Greg
1177d2263f Add --version to --help 2014-06-27 11:08:09 -04:00
Ben Schwarz
b9478a1f65 Merge pull request #1367 from abutcher/bower-install-save-readme
Add bower install --save/--save-dev blurb to README.md. Closes #1321
2014-06-25 08:03:45 +10:00
Andrew Butcher
025cf91679 README.md --save rewording suggested by @benschwarz for #1321 2014-06-24 09:33:45 -04:00
Adam Stankiewicz
442aa72ccc [fixes 5c839724] Prevent command like "bower info" from crashing on render 2014-06-24 11:39:44 +02:00
Adam Stankiewicz
1c09f9c82d Bump bower-config and endpoint-parser versions 2014-06-24 11:16:07 +02:00
Andrew Butcher
c6d2f633ea Add bower install --save/--save-dev blurb to README.md. Closes #1321 2014-06-24 00:37:57 -04:00
Adam Stankiewicz
17bd60e3e9 [#1356] Disable caching for local resources 2014-06-22 22:05:13 +02:00
Adam Stankiewicz
1d24e82276 [#931] Make --force always re-run installation 2014-06-21 00:33:51 +02:00
Adam Stankiewicz
e42d3d5620 [#1329] Check if pkgMeta is undefined 2014-06-20 21:57:41 +02:00
Thomas Scholtes
5c83972401 Use promise interface for commands
This commit changes the interface of the command functions exported by the
files in `lib/commands`. The functions now return a promise and accept a logger
as the first argument. This has several advantages

* The promise style is consistent with the rest of the code.
* It removes a lot of duplicate code.
* The command factory does not need to proxy the logger object.
2014-06-20 21:14:26 +02:00
Martin Hansen
b8df18481f Ignore does now not apply to any file in main #547 2014-06-20 19:48:00 +02:00
Andrew Eisenberg
1690dd4728 Warn users when installing package with missing properties.
Fix for issue #694. Test case included.
2014-06-20 19:00:19 +02:00
Sindre Sorhus
a86087ed7c Update README.md 2014-06-13 23:32:05 +02:00
Ray Shan
745060f0b5 Close GH-1348: Updated Analytics section of readme.md with link to stats.bower.io. 2014-06-13 23:30:12 +02:00
Sindre Sorhus
0a0a490a38 bump deps 2014-06-10 22:18:26 +02:00
Adam Stankiewicz
f816a5b0da Bump tmp version to 0.0.23, #988 2014-06-09 03:09:22 +02:00
Adam Stankiewicz
2f02e49716 Always ignore bower.json as said in spec 2014-06-08 18:46:39 +02:00
Mat Scales
a98a0b1ac2 Release 1.3.5 2014-06-06 11:31:37 +01:00
Adam Stankiewicz
034326c984 Add some tests and documentation for #1310 2014-06-05 23:52:48 +02:00
Sergey Tatarintsev
a965b05400 Search compatible versions in fetching packages
Previous version of code reported endpoints that does not have pckMeta
(not downloaded) yet and does not have exactly equal targets as
incompatible. This caused some package to be downloaded multiple times
and caused conflict errors (#1147).

This patch adds proper code for searching compatible version in
currently fetching packages basing on targets.

It also simplifies check for case when we have resolvedVersion and
candidate's target is range.
2014-06-05 19:41:37 +02:00
Mat Scales
6c2de4a359 1.3.4 2014-06-02 15:22:18 +01:00
Mat Scales
e9588279c8 Updated changelog for v1.3.4 2014-06-02 15:21:04 +01:00
Mat Scales
efe3a78499 Merge pull request #1278 from jfsiii/patch-1
Mirror `npm version` format for git tags
2014-05-27 19:37:53 +01:00
Adam Stankiewicz
31969a4939 Merge pull request #1306 from wibblymat/dependency-loop
Resolve a situation in which the install process gets into an infinite loop
2014-05-21 13:50:03 +02:00
Adam Stankiewicz
7a00c5ac71 Merge pull request #1307 from thomasmulvaney/patch-1
Fixed typo in README.md
2014-05-20 16:33:19 +02:00
Mat Scales
23fbbb5191 Resolve a situation in which the install process gets into an infinite loop 2014-05-20 14:46:41 +01:00
thomasmulvaney
a58b1ccfa5 Update README.md
Fixed sentence which had 'set manually' accidentally repeated.
2014-05-20 15:26:40 +02:00
Ben Schwarz
37b0a5c04c Merge pull request #1291 from danfinnie/patch-1
Fix Markdown typo in CONTRIBUTING.md
2014-05-12 13:12:38 +10:00
Daniel Finnie
3fb4f60192 Fix Markdown typo in CONTRIBUTING.md 2014-05-11 23:06:46 -04:00
Ben Schwarz
635fa84731 Merge pull request #1284 from niksy/improve-cli-color-output
Improve CLI output for conflicts
2014-05-08 08:14:13 +10:00
Ivan Nikolić
410bf4f0cd Improve CLI output for conflicts
Replace white color with green.
2014-05-07 19:27:44 +02:00
John Schulz
eebe115b78 Mirror npm version format for git tags
See https://github.com/bower/bower/pull/961#issuecomment-33981712 and  https://github.com/bower/bower/pull/961#issuecomment-35591108 for more context.
2014-05-04 15:11:47 -07:00
Sindre Sorhus
70eb9f0678 Merge pull request #1277 from benschwarz/analytics-statement-and-disable
Description of Analytics usage & how to disable
2014-05-04 06:03:26 +02:00
Ben Schwarz
b9de179fe8 Add notes about what analytics are used for, who has access and how to disable 2014-05-04 13:17:10 +10:00
Adam Stankiewicz
ac29e24c1b Allow for short commit SHA, closes #990 2014-04-25 02:09:10 +02:00
Adam Stankiewicz
d83572803d Bump, update changelog, update contributors 2014-04-24 22:35:22 +02:00
Adam Stankiewicz
87faa4f108 Do not cache moving targets like branches, fixes #1242 2014-04-24 20:31:36 +02:00
Sindre Sorhus
ea3a0d64c1 make jshint :) 2014-04-22 16:01:24 +02:00
Sindre Sorhus
8288d2f38b readme improvements 2014-04-22 15:28:29 +02:00
Sindre Sorhus
ff817dad0d various tweaks 2014-04-22 15:16:58 +02:00
Sindre Sorhus
ba554d5f45 use opn instead of open
`open` is buggy on Linux

https://github.com/sindresorhus/opn
2014-04-22 15:07:33 +02:00
Sindre Sorhus
90922a0ce0 bump deps 2014-04-22 15:04:42 +02:00
Sindre Sorhus
a91cb546f9 let npm sort dependencies 2014-04-22 14:57:38 +02:00
Adam Stankiewicz
3cb21d128f Forward all events from wrapped command, fixes #1232 2014-04-18 14:41:27 +02:00
Paul Irish
1b8d5d0648 Merge pull request #1234 from ahmadnassri/svn-export
using 'svn export' for efficiency. Closes #1224
2014-04-14 12:47:02 -07:00
Adam Stankiewicz
03f035cdf0 Merge pull request #1239 from bower/docs/usage
Discourage using bower components statically, closes #1210
2014-04-13 01:55:07 +02:00
Adam Stankiewicz
ac95654409 Discourage using bower components statically, closes #1210 2014-04-13 01:53:12 +02:00
Adam Stankiewicz
ba33bb6aa4 Cache clean and cache list work again, fixes #1232 2014-04-13 01:45:56 +02:00
Sindre Sorhus
2898c0507e Merge pull request #1237 from bower/fix/bower-list
Fix considering extended directory using .bowerrc
2014-04-12 23:41:32 +02:00
Sindre Sorhus
d59edd6cca Merge pull request #1238 from bower/feature/performance
Improve noninteractive loading performance 2x, fixes #1221
2014-04-12 23:33:49 +02:00
Adam Stankiewicz
74d568b1e7 Improve noninteractive loading performance 2x, fixes #1221 2014-04-12 21:11:17 +02:00
toshipon
0316fedd4c Fix considering extended directory using .bowerrc 2014-04-12 14:40:45 +02:00
Sindre Sorhus
5247d93d7b Merge pull request #1193 from chiester/wip-1191-improveUninstallWarning
clarify uninstall warning message
2014-04-11 21:45:37 +02:00
Chris Hiester
bda5312917 clarified uniinstall warning message
improved uninstall message

edits to uninstall message

revisions per conversation with core team

fixed lint errors

reverted mode changes

reverted test.js mode change
2014-04-11 12:35:28 -04:00
Ahmad Nassri
e8c071304c using 'svn export' for efficiency. Closes #1224 2014-04-11 09:00:41 -04:00
Sindre Sorhus
4e155caae9 Merge pull request #1124 from jmmk/quiet_output
Suppress end output if quiet option is specified
2014-04-11 12:11:45 +02:00
Sindre Sorhus
aedb7e44ba Merge pull request #1231 from bower/fix/dependencies
Update dependencies in package.json to newest patches
2014-04-11 12:02:25 +02:00
Sindre Sorhus
c58109c1b4 Merge pull request #1232 from bower/feature/lazy-loading
Loading commands on demand increases performance
2014-04-11 11:50:20 +02:00
Thomas Scholtes
2167c7b6d4 Loading commands on demand increases performance 2014-04-11 06:07:42 +02:00
Adam Stankiewicz
22eef989f6 Update dependencies in package.json to newest patches 2014-04-11 05:42:53 +02:00
Adam Stankiewicz
edf387363d doc: Point to new official msysgit website 2014-04-11 01:44:07 +02:00
Adam Stankiewicz
14ffb7443f Merge pull request #1230 from brettz9/readme
Refer to spec; add description in sample; fix map order
2014-04-10 02:26:29 +02:00
Brett Zamir
c21ba1e64c Refer to spec; add description in sample; fix map order 2014-04-10 08:02:18 +08:00
Sindre Sorhus
46b8069c6d Merge pull request #1227 from Aleanar/master
Update private Subversion repository example in README.md
2014-04-09 16:39:40 +02:00
Alexandre MOTTET
2fb697f452 Update private Subversion repository example in README.md 2014-04-09 13:44:22 +00:00
Ben Schwarz
73f3293314 Merge pull request #1212 from bower/release-1-3-2
Prepped 1.3.2 release
2014-04-07 11:40:45 +10:00
Ben Schwarz
53fc25550b Added yui moduletype 2014-04-07 11:35:16 +10:00
Eric Ferraiuolo
961d1775e5 Squashed commit of the following:
commit d5ab6721b6d0014b25ec7e5da2078627f0e794dd
Author: Eric Ferraiuolo <eferraiuolo@gmail.com>
Date:   Thu Feb 20 19:07:04 2014 -0500

    Add YUI modules as a `moduleType` choice

Closes gh-1129.
2014-04-06 18:13:37 -07:00
Ben Schwarz
bdaa359d11 Update changelog with #1211, remove #1077 2014-04-07 11:01:25 +10:00
Ben Schwarz
a90cc018dd Merge pull request #1216 from benschwarz/master
Revert 1077, merge 1211
2014-04-07 10:53:39 +10:00
Sindre Sorhus
e06f784d35 Merge pull request #1215 from t3chnoboy/master
Use SVG version of travis build status badge
2014-04-06 21:01:09 +02:00
Dmitry Mazuro
5ce1b6a4c1 Use SVG version of travis build status badge 2014-04-06 19:47:06 +03:00
Sindre Sorhus
86d579a7ae Merge pull request #1213 from rayshan/patch-1
Tweak insight tracking for `search` command
2014-04-06 14:15:29 +02:00
Ray Shan
d031ab5ceb Tweak insight tracking for search command
- Tracking should be consistent with `info` command; tracking should register upon user calling the command, not when the command is completed
- Vocabulary should be consistent with other tracking - if only tracking upon calling the command, should use the present tense of the verb without -"ed"

This is an improvement for tracking and reporting consistency, please see https://github.com/bower/bower/issues/1164#issuecomment-39385297
2014-04-05 23:00:38 -07:00
Ben Schwarz
dcf7230494 Merge remote-tracking branch 'sheerun/fix/concurrency' 2014-04-06 14:59:37 +10:00
Ben Schwarz
20aca45b39 Revert "Merge pull request #1077 from neoziro/fix-concurrent-renaming"
This reverts commit cd541c7a20, reversing
changes made to 3074711828.
2014-04-06 14:57:40 +10:00
Ben Schwarz
98ea43eca9 Prepped 1.3.2 release 2014-04-05 16:03:25 +11:00
Ben Schwarz
cd541c7a20 Merge pull request #1077 from neoziro/fix-concurrent-renaming
Fix bug with concurrent renaming
2014-04-05 15:41:39 +11:00
Adam Stankiewicz
5a5cf31aba Make bower concurrent friendly, fixes #933 2014-04-04 19:09:18 +02:00
Mat Scales
3074711828 Merge pull request #891 from bower/link-install-deps
Install linked package dependencies, closes #673.
2014-04-02 13:59:14 +01:00
Mat Scales
aa97c928e9 Merge pull request #1203 from wibblymat/node0.11
Add node 0.11 to Travis, but allow it to fail.
2014-04-02 13:54:52 +01:00
Mat Scales
5b4d6f2fee Add node 0.11 to Travis, but allow it to fail. 2014-04-02 12:55:00 +01:00
André Cruz
7120ad5014 Merge branch 'master' of github.com:bower/bower into link-install-deps 2014-03-25 11:51:40 +00:00
André Cruz
6286cfac28 Merge branch 'master' into link-install-deps 2014-03-25 11:49:05 +00:00
Sindre Sorhus
1af09da2aa Merge pull request #1188 from chiester/wip-1187-conflictResolvedMessage
Improved conflict resolution message
2014-03-25 09:58:28 +01:00
Chris Hiester
bea533acf8 Improved conflict resolution message
removed test-generated npm-debug.log

fixed semver formatting in message
2014-03-24 13:48:25 -04:00
Mat Scales
5316c2479e Merge pull request #1168 from c960657/prune-production
Add --production switch to "prune" command
2014-03-19 16:37:29 +00:00
Andrey 'lolmaus' Mikhaylov
9895f8c71e readme - links to the range syntax of version numbers
closes #1172
2014-03-14 17:50:11 +01:00
Christian Schmidt
9749a398c0 Add --production switch to "prune" command 2014-03-12 18:38:17 +01:00
Ben Schwarz
2883a278ee Formatting tweaks to the CI section 2014-03-12 10:58:41 +11:00
Ben Schwarz
6bbb9ec956 Merge pull request #1165 from nschonni/ci-readme-update
Add additional CI examples
2014-03-12 10:54:47 +11:00
Mat Scales
630556f7fe Release 1.3.1 2014-03-11 23:39:39 +00:00
Nick Schonning
32a79c4fa1 Add additional CI examples 2014-03-11 19:33:26 -04:00
Paul Irish
eabfee6890 Merge pull request #1163 from nschonni/no-analytics-on-ci
Don't turn on analytics on CI unless explicitly set
2014-03-11 16:27:12 -07:00
Nick Schonning
6b9b283149 Add CI server configuration notes 2014-03-11 19:02:43 -04:00
Paul Irish
a19ad6663c insight in changelog 2014-03-11 13:52:35 -07:00
Nick Schonning
dfae97a8d7 Don't turn on analytics on CI unless explicit 2014-03-11 16:51:41 -04:00
Mat Scales
93d03434eb Merge pull request #1152 from ahmadnassri/svnResolver-test-fix
svnResolver updates
2014-03-11 16:06:54 +00:00
Ahmad Nassri
cfc061c960 Merge branch 'svn-resolver-fixes' of github.com:jevakallio/bower into svnResolver-test-fix 2014-03-05 14:10:08 -05:00
Ahmad Nassri
09bc1784e4 Fixing svnResolver tests
- duplicate 0.0.1 tag in json config
- `svn list` return directory separator at the end of branch/tag names
2014-03-05 13:51:43 -05:00
jevakallio
b049bdb33f Fix problem with dashes and underscores in branch/tag names 2014-02-27 17:31:01 +02:00
Michael McLellan
cc63a633b5 Suppress end output if quiet option is specified 2014-02-18 17:13:04 -05:00
Greg Bergé
8016bd4e1f Use new Q() shortcut. 2014-02-13 16:30:39 +01:00
Greg Bergé
d130c73e77 Fix removing directory. 2014-01-28 12:23:21 +01:00
Greg Bergé
3ecc389e66 Fix concurrent renaming.
We stock the renaming promise in cache to ensure that there is not concurrent renaming.
2014-01-27 23:56:22 +01:00
André Cruz
99b0c8974b Merge branch 'master' of github.com:bower/bower into link-install-deps 2013-10-04 12:10:49 +01:00
André Cruz
194a09f21d Some fixes. 2013-09-30 18:25:54 +01:00
André Cruz
b87b8ea931 Install linked package dependencies, closes #673.
Also add a "unlink" alias to "uninstall".
2013-09-30 02:33:06 +01:00
80 changed files with 2409 additions and 1266 deletions

View File

@@ -13,3 +13,7 @@ trim_trailing_whitespace = false
[**.std]
insert_final_newline = false
[{package,bower}.json]
indent_style = space
indent_size = 2

5
.gitignore vendored
View File

@@ -1,11 +1,10 @@
/node_modules
/npm-debug.log
/test/assets/temp
/test/assets/temp2
/test/assets/temp-resolve-cache
/test/assets/package-*/
/test/assets/temp-*/
/test/reports
/test/tmp/
/bower.json
/component.json

View File

@@ -1,5 +1,9 @@
language: node_js
node_js:
- "0.10"
before_script:
- npm install grunt-cli -g
- '0.10'
- '0.11'
matrix:
allow_failures:
- node_js: '0.11'
script:
- grunt travis

View File

@@ -1,9 +1,88 @@
# Changelog
## 1.3.11 - 2014-09-17
- [fix] Restore install missing dependencies on update ([1519](https://github.com/bower/bower/pull/1519))
## 1.3.10 - 2014-09-13
- [fix] Back down concurrency from 50 to 5 ([#1483](https://github.com/bower/bower/pull/1483))
- [fix] Read .bowerrc from specified cwd ([#1301](https://github.com/bower/bower/pull/1301))
- [fix] Disable shallow clones except those from GitHub ([#1393](https://github.com/bower/bower/pull/1393))
- [fix] Expose bower version ([#1478](https://github.com/bower/bower/pull/1478))
- [fix] Bump dependencies, including "request" ([#1467](https://github.com/bower/bower/pull/1467))
- [fix] Prevent an error when piping bower output to head ([#1508](https://github.com/bower/bower/pull/1508))
- [fix] Disable removing unnecessary resolutions ([#1061](https://github.com/bower/bower/pull/1061))
- [fix] Display the output of hooks again ([#1484](https://github.com/bower/bower/issues/1484))
- [fix] analytics: true in .bowerrc prevents user prompt ([#1470](https://github.com/bower/bower/pull/1470))
- [perf] Use `tar-fs` instead of `tar` for faster TAR extraction ([#1490](https://github.com/bower/bower/pull/1490))
## 1.3.9 - 2014-08-06
- [fix] Handle `tmp` sometimes returning an array ([#1434](https://github.com/bower/bower/pull/1434))
## 1.3.8 - 2014-7-11
- [fix] Lock down `tmp` package dep ([#1403](https://github.com/bower/bower/pull/1403), [#1407](https://github.com/bower/bower/pull/1407))
## 1.3.7 - 2014-07-04
- [fix] callstack error when processing installed packages with circular dependencies ([#1349](https://github.com/bower/bower/issues/1349))
- [fix] Prevent bower list --paths` failing with TypeError ([#1383](https://github.com/bower/bower/issues/1383))
- "bower install" fails if there's no bower.json in current directory ([#922](https://github.com/bower/bower/issues/922))
## 1.3.6 - 2014-07-02
- [fix] Make --force always re-run installation ([#931](https://github.com/bower/bower/issues/931))
- [fix] Disable caching for local resources ([#1356](https://github.com/bower/bower/issues/1356))
- [fix] Emit errors instead throwing them when using bower.commands API ([#1297](https://github.com/bower/bower/issues/1297))
- [fix] Main files and bower.json are never ignored ([#547](https://github.com/bower/bower/issues/547))
- [fix] Check if pkgMeta is undefined during uninstall command ([#1329](https://github.com/bower/bower/issues/1329))
- [fix] Make custom tmp dir and ignores play well with each other ([#1299](https://github.com/bower/bower/issues/1299))
- Warn users when installing package with missing properties ([#694](https://github.com/bower/bower/issues/694))
## 1.3.5 - 2014-06-06
- Search compatible versions in fetching packages ([#1147](https://github.com/bower/bower/issues/1147))
## 1.3.4 - 2014-06-02
- Resolve a situation in which the install process gets into an infinite loop ([#1169](https://github.com/bower/bower/issues/1169))
- Improved CLI output for conflicts ([#1284](https://github.com/bower/bower/issues/1284))
- Changed `bower version` to mirror the tag format of `npm version` ([#1278](https://github.com/bower/bower/issues/1278))
- Allow short commit SHAs to be used ([#990](https://github.com/bower/bower/issues/990))
## 1.3.3 - 2014-04-24
- Do not cache moving targets like branches ([#1242](https://github.com/bower/bower/issues/1242))
- Suppress output if --quiet option is specified ([#1124](https://github.com/bower/bower/pull/1124))
- Use "svn export" for efficiency ([#1224](https://github.com/bower/bower/pull/1224))
- Prevent loading insights and analytics on CI ([#1221](https://github.com/bower/bower/issues/1221))
- Make "bower list" respect custom components directory ([#1237](https://github.com/bower/bower/issues/1237))
- Improve non-interactive loading performance 2x ([#1238](https://github.com/bower/bower/issues/1238))
- Load commands only on demand, improving performance ([#1232](https://github.com/bower/bower/pull/1232))
## 1.3.2 - 2014-04-05
- Added yui moduleType [PR #1129](https://github.com/bower/bower/pull/1129)
- Fixes for concurrency issues [PR #1211](https://github.com/bower/bower/pull/1211)
- `link` now installs package dependencies [PR #891](https://github.com/bower/bower/pull/891)
- Improved conflict installation message [Commit](https://github.com/bower/bower/commit/bea533acf87903d4b411bfbaa7df93f852ef46a3)
- Add --production switch to "prune" command [PR #1168](https://github.com/bower/bower/pull/1168)
## 1.3.1 - 2014-03-10
- No longer ask for permission to gather analytics when running on in a CI environment.
## 1.3.0 - 2014-03-10
- **Removed support for node 0.8.** It may still work but we will no longer fix bugs for older versions of node.
- Add **Bower Insight** for opt-in analytics integration to help improve tool and gain insight on community trends
- Old overview of [Insight](https://github.com/yeoman/yeoman/wiki/Insight), [Issue #260](https://github.com/bower/bower/issues/260)
- Reporting to GA. Public Dashboard is in progress.
- [Turn off interactive mode](https://github.com/bower/bower/issues/1162) if you run Bower in a CI environment
- Add `moduleType` property to bower init ([#934](https://github.com/bower/bower/pull/934))
- Fix prune command to log only after cleanup is completed ([#1023](https://github.com/bower/bower/issues/1023))
- Fix git resolver to ignore pre-release versions ([#1017](https://github.com/bower/bower/issues/1017))

View File

@@ -33,8 +33,6 @@ changes, and helping you finalize your pull requests.
## Using the issue tracker
*
The issue tracker is the preferred channel for [bug reports](#bugs),
[features requests](#features) and [submitting pull
requests](#pull-requests), but please respect the following restrictions:

View File

@@ -1,5 +1,5 @@
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
@@ -7,14 +7,24 @@ module.exports = function (grunt) {
options: {
jshintrc: '.jshintrc'
},
files: ['Gruntfile.js', 'bin/*', 'lib/**/*.js', 'test/**/*.js', '!test/assets/**/*', '!test/reports/**/*']
files: [
'Gruntfile.js',
'bin/*',
'lib/**/*.js',
'test/**/*.js',
'!test/assets/**/*',
'!test/reports/**/*',
'!test/tmp/**/*'
]
},
simplemocha: {
options: {
reporter: 'spec',
timeout: '5000'
},
full: { src: ['test/test.js'] },
full: {
src: ['test/test.js']
},
short: {
options: {
reporter: 'dot'
@@ -30,7 +40,10 @@ module.exports = function (grunt) {
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 -- -R dot test/test.js'
command: 'STRICT_REQUIRE=1 node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- -R dot test/test.js'
},
coveralls: {
command: 'node node_modules/.bin/coveralls < test/reports/lcov.info'
}
},
watch: {
@@ -39,9 +52,9 @@ module.exports = function (grunt) {
}
});
grunt.registerTask('assets', ['exec:assets-force']);
grunt.registerTask('test', ['jshint', 'exec:assets', 'simplemocha:full']);
grunt.registerTask('cover', 'exec:cover');
grunt.registerTask('travis', ['jshint', 'exec:assets', 'exec:cover', 'exec:coveralls']);
grunt.registerTask('default', 'test');
};

345
README.md
View File

@@ -1,152 +1,73 @@
# Bower
[![Build Status](https://secure.travis-ci.org/bower/bower.png?branch=master)](http://travis-ci.org/bower/bower)
[![Build Status](https://travis-ci.org/bower/bower.svg?branch=master)](https://travis-ci.org/bower/bower) [![Coverage Status](https://coveralls.io/repos/bower/bower/badge.png?branch=master)](https://coveralls.io/r/bower/bower?branch=master)
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
Bower is a package manager for the web. It offers a generic, unopinionated
solution to the problem of **front-end package management**, while exposing the
package dependency model via an API that can be consumed by a more opinionated
build stack. There are no system wide dependencies, no dependencies are shared
between different apps, and the dependency tree is flat.
> A package manager for the web
Bower runs over Git, and is package-agnostic. A packaged component can be made
up of any type of asset, and use any type of transport (e.g., AMD, CommonJS,
etc.).
Bower offers a generic, unopinionated solution to the problem of **front-end package management**, while exposing the package dependency model via an API that can be consumed by a more opinionated build stack. There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
Bower runs over Git, and is package-agnostic. A packaged component can be made up of any type of asset, and use any type of transport (e.g., AMD, CommonJS, etc.).
**View complete docs on [bower.io](http://bower.io)**
[View all packages available through Bower's registry](http://bower.io/search/).
## Install
## Installing Bower
Bower depends on [Node](http://nodejs.org/) and [npm](http://npmjs.org/). It's
installed globally using npm:
```
npm install -g bower
```sh
$ npm install -g bower
```
Also make sure that [git](http://git-scm.com/) is installed as some bower
Bower depends on [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/). Also make sure that [git](http://git-scm.com/) is installed as some bower
packages require it to be fetched and installed.
## Usage
Much more information is available via `bower help` once it's installed. This
is just enough to get you started.
See complete command line reference at [bower.io/docs/api/](http://bower.io/docs/api/)
### Installing packages and dependencies
Bower offers several ways to install packages:
```sh
# install dependencies listed in bower.json
$ bower install
#####Using the dependencies listed in the current directory's bower.json
# install a package and add it to bower.json
$ bower install <package> --save
# install specific version of a package and add it to bower.json
$ bower install <package>#<version> --save
```
bower install
```
##### Using a local or remote package
```
bower install <package>
```
##### Using a specific version of a package
```
bower install <package>#<version>
```
##### Using a different name and a specific version of a package
```
bower install <name>=<package>#<version>
```
Where `<package>` can be any one of the following:
* A name that maps to a package registered with Bower, e.g, `jquery`. ‡
* A public remote Git endpoint, e.g., ```git://github.com/someone/some-package.git```. ‡
* A private Git repository, e.g., ```https://github.com/someone/some-package.git```. If the protocol is https, a prompt will ask for the credentials. ssh can also be used, e.g., ```git@github.com:someone/some-package.git``` and can authenticate with the user's ssh public/private keys. ‡
* A local endpoint, i.e., a folder that's a Git repository. ‡
* A public remote Subversion endpoint, e.g., ```svn+http://package.googlecode.com/svn/```. ‡
* A private Subversion repository, e.g., ```svn+ssh://package.googlecode.com/svn/```. ‡
* A local endpoint, i.e., a folder that's an Subversion repository, e.g., ```svn+file:///path/to/svn/```. ‡
* A shorthand endpoint, e.g., `someone/some-package` (defaults to GitHub). ‡
* A URL to a file, including `zip` and `tar` files. Its contents will be
extracted.
‡ These types of `<package>` might have versions available. You can specify a
[semver](http://semver.org/) compatible version to fetch a specific release, and lock the
package to that version. You can also use ranges to specify a range of versions.
If you are using a package that is a git endpoint, you may use any tag, commit SHA,
or branch name as a version. For example: `<package>#<sha>`. Using branches is not
recommended because the HEAD does not reference a fixed commit SHA.
If you are using a package that is a subversion endpoint, you may use any tag, revision number,
or branch name as a version. For example: `<package>#<revision>`.
All package contents are installed in the `bower_components` directory by default.
You should **never** directly modify the contents of this directory.
Using `bower list` will show all the packages that are installed locally.
**N.B.** If you aren't authoring a package that is intended to be consumed by
others (e.g., you're building a web app), you should always [check installed
packages into source control](http://addyosmani.com/blog/checking-in-front-end-dependencies/).
### Custom install directory
A custom install location can be set in a `.bowerrc` file using the `directory` property. The .bowerrc file should be a sibling of your project's bower.json.
```json
{
"directory": "public/bower_components"
}
```
### Finding packages
To search for packages registered with Bower:
```
bower search [<name>]
```
Using just `bower search` will list all packages in the registry.
### Using packages
The easiest approach is to use Bower statically, just reference the package's
installed components manually using a `script` tag:
We discourage using bower components statically for performance and security reasons (if component has an `upload.php` file that is not ignored, that can be easily exploited to do malicious stuff).
```html
<script src="/bower_components/jquery/jquery.js"></script>
```
For more complex projects, you'll probably want to concatenate your scripts or
use a module loader. Bower is just a package manager, but there are plenty of
other tools -- such as [Sprockets](https://github.com/sstephenson/sprockets)
and [RequireJS](http://requirejs.org/) -- that will help you do this.
The best approach is to process components installed by bower with build tool (like [Grunt](http://gruntjs.com/) or [gulp](http://gulpjs.com/)), and serve them concatenated or using module loader (like [RequireJS](http://requirejs.org/)).
### Uninstalling packages
To uninstall a locally installed package:
```
bower uninstall <package-name>
```sh
$ bower uninstall <package-name>
```
#### Warning
### prezto and oh-my-zsh users
On `prezto` or `oh-my-zsh`, do not forget to `alias bower='noglob bower'` or `bower install jquery\#1.9.1`
#### Running commands with sudo
### Running commands with sudo
Bower is a user command, there is no need to execute it with superuser permissions.
However, if you still want to run commands with sudo, use `--allow-root` option.
#### A note for Windows users
### Windows users
To use Bower on Windows, you must install
[msysgit](http://code.google.com/p/msysgit/) correctly. Be sure to check the
[msysgit](http://msysgit.github.io/) correctly. Be sure to check the
option shown below:
![msysgit](http://f.cl.ly/items/2V2O3i1p3R2F1r2v0a12/mysgit.png)
@@ -156,170 +77,9 @@ password, you should add the following environment variable: `GIT_SSH -
C:\Program Files\TortoiseGit\bin\TortoisePlink.exe`. Adjust the `TortoisePlink`
path if needed.
### Using bower's cache
Bower supports installing packages from its local cache (without internet connection), if the packages were installed before.
```
bower install <package-name> --offline
```
The content of the cache can be listed with:
```
bower cache list
```
The cache can be cleaned with:
```
bower cache clean
```
## Configuration
Bower can be configured using JSON in a `.bowerrc` file.
The current spec can be read
[here](https://docs.google.com/document/d/1APq7oA9tNao1UYWyOm8dKqlRP2blVkROYLZ2fLIjtWc/edit#heading=h.4pzytc1f9j8k)
in the `Configuration` section.
## Defining a package
You must create a `bower.json` in your project's root, and specify all of its
dependencies. This is similar to Node's `package.json`, or Ruby's `Gemfile`,
and is useful for locking down a project's dependencies.
*NOTE:* In versions of Bower before 0.9.0 the package metadata file was called
`component.json` rather than `bower.json`. This has changed to avoid a name
clash with another tool. You can still use `component.json` for now but it is
deprecated and the automatic fallback is likely to be removed in an upcoming
release.
You can interactively create a `bower.json` with the following command:
```
bower init
```
The `bower.json` defines several options:
* `name` (required): The name of your package.
* `version`: A semantic version number (see [semver](http://semver.org/)).
* `main` [string|array]: The primary endpoints of your package.
* `ignore` [array]: An array of paths not needed in production that you want
Bower to ignore when installing your package.
* `dependencies` [hash]: Packages your package depends upon in production.
* `devDependencies` [hash]: Development dependencies.
* `private` [boolean]: Set to true if you want to keep the package private and
do not want to register the package in future.
```json
{
"name": "my-project",
"version": "1.0.0",
"main": "path/to/main.css",
"ignore": [
".jshintrc",
"**/*.txt"
],
"dependencies": {
"<name>": "<version>",
"<name>": "<folder>",
"<name>": "<package>"
},
"devDependencies": {
"<test-framework-name>": "<version>"
}
}
```
### Registering packages
To register a new package:
* There **must** be a valid manifest JSON in the current working directory.
* Your package should use [semver](http://semver.org/) Git tags.
* Your package **must** be available at a Git endpoint (e.g., GitHub); remember
to push your Git tags!
Then use the following command:
```
bower register <my-package-name> <git-endpoint>
```
The Bower registry does not have authentication or user management at this point
in time. It's on a first come, first served basis. Think of it like a URL
shortener. Now anyone can run `bower install <my-package-name>`, and get your
library installed.
There is no direct way to unregister a package yet. For now, you can [request a
package be unregistered](https://github.com/bower/bower/issues/120).
## Consuming a package
Bower also makes available a source mapping. This can be used by build tools to
easily consume Bower packages.
If you pass the `--paths` option to Bower's `list` command, you will get a
simple path-to-name mapping:
```json
{
"backbone": "bower_components/backbone/index.js",
"jquery": "bower_components/jquery/index.js",
"underscore": "bower_components/underscore/index.js"
}
```
Alternatively, every command supports the `--json` option that makes bower
output JSON. Command result is outputted to `stdout` and error/logs to
`stderr`.
## Programmatic API
Bower provides a powerful, programmatic API. All commands can be accessed
through the `bower.commands` object.
```js
var bower = require('bower');
bower.commands
.install(['jquery'], { save: true }, { /* custom config */ })
.on('end', function (installed) {
console.log(installed);
});
bower.commands
.search('jquery', {})
.on('end', function (results) {
console.log(results);
});
```
Commands emit four types of events: `log`, `prompt`, `end`, `error`.
* `log` is emitted to report the state/progress of the command.
* `prompt` is emitted whenever the user needs to be prompted.
* `error` will only be emitted if something goes wrong.
* `end` is emitted when the command successfully ends.
For a better of idea how this works, you may want to check out [our bin
file](https://github.com/bower/bower/blob/master/bin/bower).
When using bower programmatically, prompting is disabled by default. Though you can enable it when calling commands with `interactive: true` in the config.
This requires you to listen for the `prompt` event and handle the prompting yourself. The easiest way is to use the [inquirer](https://npmjs.org/package/inquirer) npm module like so:
```js
var inquirer = require('inquirer');
bower.commands
.install(['jquery'], { save: true }, { interactive: true })
// ..
.on('prompt', function (prompts, callback) {
inquirer.prompt(prompts, callback);
});
```
Bower can be configured using JSON in a `.bowerrc` file. Read over available options at [bower.io/docs/config](http://bower.io/docs/config).
## Completion (experimental)
@@ -332,23 +92,21 @@ not available for Windows users.
This command will output a Bash / ZSH script to put into your `~/.bashrc`,
`~/.bash_profile`, or `~/.zshrc` file.
```
bower completion >> ~/.bash_profile
```sh
$ bower completion >> ~/.bash_profile
```
## Contact
Have a question?
## Support
* [StackOverflow](http://stackoverflow.com/questions/tagged/bower)
* [Mailinglist](http://groups.google.com/group/twitter-bower) - twitter-bower@googlegroups.com
* [\#bower](http://webchat.freenode.net/?channels=bower) on Freenode
## Contributing to this project
## Contributing
Anyone and everyone is welcome to contribute. Please take a moment to
We welcome contributions of all kinds from anyone. Please take a moment to
review the [guidelines for contributing](CONTRIBUTING.md).
* [Bug reports](CONTRIBUTING.md#bugs)
@@ -358,6 +116,8 @@ review the [guidelines for contributing](CONTRIBUTING.md).
## Bower Team
Bower is made by lots of people across the globe, contributions large and small. Our thanks to everyone who has played a part.
### Core team
* [@satazor](https://github.com/satazor)
@@ -366,38 +126,7 @@ review the [guidelines for contributing](CONTRIBUTING.md).
* [@benschwarz](https://github.com/benschwarz)
* [@sindresorhus](https://github.com/sindresorhus)
* [@svnlto](https://github.com/svnlto)
Thanks for assistance and contributions:
[@addyosmani](https://github.com/addyosmani),
[@ahmadnassri](https://github.com/ahmadnassri),
[@angus-c](https://github.com/angus-c),
[@borismus](https://github.com/borismus),
[@carsonmcdonald](https://github.com/carsonmcdonald),
[@chriseppstein](https://github.com/chriseppstein),
[@danwrong](https://github.com/danwrong),
[@davidmaxwaterman](https://github.com/davidmaxwaterman),
[@desandro](https://github.com/desandro),
[@hemanth](https://github.com/hemanth),
[@isaacs](https://github.com/isaacs),
[@josh](https://github.com/josh),
[@jrburke](https://github.com/jrburke),
[@kennethklee](https://github.com/kennethklee),
[@marcelombc](https://github.com/marcelombc),
[@marcooliveira](https://github.com/marcooliveira),
[@mklabs](https://github.com/mklabs),
[@MrDHat](https://github.com/MrDHat),
[@necolas](https://github.com/necolas),
[@richo](https://github.com/richo),
[@rvagg](https://github.com/rvagg),
[@ryanflorence](https://github.com/ryanflorence),
[@SlexAxton](https://github.com/SlexAxton),
[@sstephenson](https://github.com/sstephenson),
[@tomdale](https://github.com/tomdale),
[@uzquiano](https://github.com/uzquiano),
[@visionmedia](https://github.com/visionmedia),
[@wagenet](https://github.com/wagenet),
[@wycats](https://github.com/wycats)
* [@sheerun](https://github.com/sheerun)
### Bower Alumni

View File

@@ -1,28 +1,24 @@
#!/usr/bin/env node
'use strict';
process.bin = process.title = 'bower';
var Q = require('q');
var path = require('path');
var mout = require('mout');
var updateNotifier = require('update-notifier');
var Logger = require('bower-logger');
var osenv = require('osenv');
var bower = require('../lib');
var pkg = require(path.join(__dirname, '..', 'package.json'));
var pkg = require('../package.json');
var cli = require('../lib/util/cli');
var rootCheck = require('../lib/util/rootCheck');
var analytics = require('../lib/util/analytics');
// --------
var options;
var renderer;
var loglevel;
var command;
var commandFunc;
var logger;
var notifier;
var levels = Logger.LEVELS;
options = cli.readOptions({
@@ -73,7 +69,7 @@ while (options.argv.remain.length) {
}
// Ask for Insights on first run.
analytics.setup().then(function () {
analytics.setup(bower.config).then(function () {
// Execute the command
commandFunc = command && mout.object.get(bower.commands, command);
command = command && command.replace(/\./g, ' ');
@@ -105,7 +101,7 @@ analytics.setup().then(function () {
logger
.on('end', function (data) {
if (!bower.config.silent) {
if (!bower.config.silent && !bower.config.quiet) {
renderer.end(data);
}
})
@@ -133,13 +129,17 @@ analytics.setup().then(function () {
logger.warn('no-home', 'HOME not set, user configuration will not be loaded');
}
// Check for newer version of Bower
notifier = updateNotifier({
packageName: pkg.name,
packageVersion: pkg.version
});
if (bower.config.interactive) {
var updateNotifier = require('update-notifier');
if (notifier.update && levels.info >= loglevel) {
renderer.updateNotice(notifier.update);
// Check for newer version of Bower
var notifier = updateNotifier({
packageName: pkg.name,
packageVersion: pkg.version
});
if (notifier.update && levels.info >= loglevel) {
notifier.notify();
}
}
});

View File

@@ -3,20 +3,18 @@ var path = require('path');
var mout = require('mout');
var Q = require('q');
var rimraf = require('rimraf');
var Logger = require('bower-logger');
var endpointParser = require('bower-endpoint-parser');
var PackageRepository = require('../../core/PackageRepository');
var semver = require('../../util/semver');
var cli = require('../../util/cli');
var defaultConfig = require('../../config');
function clean(endpoints, options, config) {
var logger = new Logger();
function clean(logger, endpoints, options, config) {
var decEndpoints;
var names;
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
// If endpoints is an empty array, null them
if (endpoints && !endpoints.length) {
@@ -33,21 +31,14 @@ function clean(endpoints, options, config) {
});
}
Q.all([
return Q.all([
clearPackages(decEndpoints, config, logger),
clearLinks(names, config, logger),
!names ? clearCompletion(config, logger) : null
])
.spread(function (entries) {
return entries;
})
.done(function (entries) {
logger.emit('end', entries);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
function clearPackages(decEndpoints, config, logger) {
@@ -201,13 +192,10 @@ function clearCompletion(config, logger) {
// -------------------
clean.line = function (argv) {
var options = clean.options(argv);
return clean(options.argv.remain.slice(2), options);
};
clean.options = function (argv) {
return cli.readOptions(argv);
clean.line = function (logger, argv) {
var options = cli.readOptions(argv);
var endpoints = options.argv.remain.slice(2);
return clean(logger, endpoints, options);
};
clean.completion = function () {

View File

@@ -1,4 +0,0 @@
module.exports = {
clean: require('./clean'),
list: require('./list')
};

View File

@@ -1,14 +1,12 @@
var mout = require('mout');
var Logger = require('bower-logger');
var PackageRepository = require('../../core/PackageRepository');
var cli = require('../../util/cli');
var defaultConfig = require('../../config');
function list(packages, options, config) {
function list(logger, packages, options, config) {
var repository;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
repository = new PackageRepository(config, logger);
// If packages is an empty array, null them
@@ -16,7 +14,7 @@ function list(packages, options, config) {
packages = null;
}
repository.list()
return repository.list()
.then(function (entries) {
if (packages) {
// Filter entries according to the specified packages
@@ -28,25 +26,15 @@ function list(packages, options, config) {
}
return entries;
})
.done(function (entries) {
logger.emit('end', entries);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
// -------------------
list.line = function (argv) {
var options = list.options(argv);
return list(options.argv.remain.slice(2), options);
};
list.options = function (argv) {
return cli.readOptions(argv);
list.line = function (logger, argv) {
var options = cli.readOptions(argv);
var packages = options.argv.remain.slice(2);
return list(logger, packages, options);
};
list.completion = function () {

View File

@@ -1,27 +1,17 @@
var Logger = require('bower-logger');
var Q = require('q');
var cli = require('../util/cli');
function completion(config) {
var logger = new Logger();
process.nextTick(function () {
logger.emit('end');
});
return logger;
return new Q();
}
// -------------------
completion.line = function (argv) {
var options = completion.options(argv);
completion.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
return completion(name);
};
completion.options = function (argv) {
return cli.readOptions(argv);
return completion(logger, name);
};
completion.completion = function () {

View File

@@ -1,12 +1,11 @@
var Q = require('q');
var path = require('path');
var fs = require('graceful-fs');
var Logger = require('bower-logger');
var cli = require('../util/cli');
var createError = require('../util/createError');
function help(name) {
function help(logger, name) {
var json;
var logger = new Logger();
if (name) {
json = path.resolve(__dirname, '../../templates/json/help-' + name.replace(/\s+/g, '/') + '.json');
@@ -14,35 +13,26 @@ function help(name) {
json = path.resolve(__dirname, '../../templates/json/help.json');
}
fs.exists(json, function (exists) {
return Q.promise(function (resolve) {
fs.exists(json, resolve);
})
.then(function (exists) {
if (!exists) {
return logger.emit('error', createError('Unknown command: ' + name, 'EUNKOWNCMD', {
throw createError('Unknown command: ' + name, 'EUNKOWNCMD', {
command: name
}));
});
}
try {
json = require(json);
} catch (error) {
return logger.emit('error', error);
}
logger.emit('end', json);
return require(json);
});
return logger;
}
// -------------------
help.line = function (argv) {
var options = help.options(argv);
return help(options.argv.remain.slice(1).join(' '));
};
help.options = function (argv) {
return cli.readOptions(argv);
help.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain.slice(1).join(' ');
return help(logger, name);
};
help.completion = function () {

View File

@@ -1,19 +1,16 @@
var mout = require('mout');
var Logger = require('bower-logger');
var Project = require('../core/Project');
var open = require('open');
var open = require('opn');
var endpointParser = require('bower-endpoint-parser');
var cli = require('../util/cli');
var createError = require('../util/createError');
var defaultConfig = require('../config');
function home(name, config) {
function home(logger, name, config) {
var project;
var promise;
var decEndpoint;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
project = new Project(config, logger);
// Get the package meta
@@ -37,7 +34,7 @@ function home(name, config) {
}
// Get homepage and open it
promise.then(function (pkgMeta) {
return promise.then(function (pkgMeta) {
var homepage = pkgMeta.homepage;
if (!homepage) {
@@ -46,27 +43,16 @@ function home(name, config) {
open(homepage);
return homepage;
})
.done(function (homepage) {
logger.emit('end', homepage);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
// -------------------
home.line = function (argv) {
var options = home.options(argv);
home.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
return home(name);
};
home.options = function (argv) {
return cli.readOptions(argv);
return home(logger, name);
};
home.completion = function () {

View File

@@ -1,18 +1,71 @@
var Q = require('q');
var Logger = require('bower-logger');
/**
* Require commands only when called.
*
* Running `commandFactory(id)` is equivalent to `require(id)`. Both calls return
* a command function. The difference is that `cmd = commandFactory()` and `cmd()`
* return as soon as possible and load and execute the command asynchronously.
*/
function commandFactory(id) {
if (process.env.STRICT_REQUIRE) {
require(id);
}
function command() {
var commandArgs = [].slice.call(arguments);
return withLogger(function (logger) {
commandArgs.unshift(logger);
return require(id).apply(undefined, commandArgs);
});
}
function runFromArgv(argv) {
return withLogger(function (logger) {
return require(id).line.call(undefined, logger, argv);
});
}
function withLogger(func) {
var logger = new Logger();
Q.try(func, logger)
.done(function () {
var args = [].slice.call(arguments);
args.unshift('end');
logger.emit.apply(logger, args);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
command.line = runFromArgv;
return command;
}
module.exports = {
cache: require('./cache'),
completion: require('./completion'),
help: require('./help'),
home: require('./home'),
info: require('./info'),
init: require('./init'),
install: require('./install'),
link: require('./link'),
list: require('./list'),
lookup: require('./lookup'),
prune: require('./prune'),
register: require('./register'),
search: require('./search'),
update: require('./update'),
uninstall: require('./uninstall'),
version: require('./version')
cache: {
clean: commandFactory('./cache/clean'),
list: commandFactory('./cache/list'),
},
completion: commandFactory('./completion'),
help: commandFactory('./help'),
home: commandFactory('./home'),
info: commandFactory('./info'),
init: commandFactory('./init'),
install: commandFactory('./install'),
link: commandFactory('./link'),
list: commandFactory('./list'),
lookup: commandFactory('./lookup'),
prune: commandFactory('./prune'),
register: commandFactory('./register'),
search: commandFactory('./search'),
update: commandFactory('./update'),
uninstall: commandFactory('./uninstall'),
version: commandFactory('./version')
};

View File

@@ -1,26 +1,24 @@
var mout = require('mout');
var Q = require('q');
var Logger = require('bower-logger');
var endpointParser = require('bower-endpoint-parser');
var PackageRepository = require('../core/PackageRepository');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function info(endpoint, property, config) {
function info(logger, endpoint, property, config) {
var repository;
var decEndpoint;
var tracker;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
repository = new PackageRepository(config, logger);
tracker = new Tracker(config);
decEndpoint = endpointParser.decompose(endpoint);
tracker.trackDecomposedEndpoints('info', [decEndpoint]);
Q.all([
return Q.all([
getPkgMeta(repository, decEndpoint, property),
decEndpoint.target === '*' && !property ? repository.versions(decEndpoint.source) : null
])
@@ -34,14 +32,7 @@ function info(endpoint, property, config) {
}
return pkgMeta;
})
.done(function (result) {
logger.emit('end', result);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
function getPkgMeta(repository, decEndpoint, property) {
@@ -62,20 +53,16 @@ function getPkgMeta(repository, decEndpoint, property) {
// -------------------
info.line = function (argv) {
var options = info.options(argv);
info.line = function (logger, argv) {
var options = cli.readOptions(argv);
var pkg = options.argv.remain[1];
var property = options.argv.remain[2];
if (!pkg) {
return null;
return new Q();
}
return info(pkg, property);
};
info.options = function (argv) {
return cli.readOptions(argv);
return info(logger, pkg, property);
};
info.completion = function () {

View File

@@ -2,7 +2,6 @@ var mout = require('mout');
var fs = require('graceful-fs');
var path = require('path');
var Q = require('q');
var Logger = require('bower-logger');
var endpointParser = require('bower-endpoint-parser');
var Project = require('../core/Project');
var defaultConfig = require('../config');
@@ -12,11 +11,10 @@ var cli = require('../util/cli');
var cmd = require('../util/cmd');
var createError = require('../util/createError');
function init(config) {
function init(logger, config) {
var project;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
// This command requires interactive to be enabled
if (!config.interactive) {
@@ -31,7 +29,7 @@ function init(config) {
project = new Project(config, logger);
// Start with existing JSON details
readJson(project, logger)
return readJson(project, logger)
// Fill in defaults
.then(setDefaults.bind(null, config))
// Now prompt user to make changes
@@ -41,14 +39,7 @@ function init(config) {
// Set dependencies based on the response
.spread(setDependencies.bind(null, project))
// All done!
.spread(saveJson.bind(null, project, logger))
.done(function (json) {
logger.emit('end', json);
}, function (error) {
logger.emit('error', error);
});
return logger;
.spread(saveJson.bind(null, project, logger));
}
function readJson(project, logger) {
@@ -205,7 +196,7 @@ function promptUser(logger, json) {
'name': 'moduleType',
'message': 'what types of modules does this package expose?',
'type': 'checkbox',
'choices': ['amd', 'es6', 'globals', 'node']
'choices': ['amd', 'es6', 'globals', 'node', 'yui']
},
{
'name': 'keywords',
@@ -329,12 +320,9 @@ function setDependencies(project, json, answers) {
// -------------------
init.line = function () {
return init();
};
init.options = function (argv) {
return cli.readOptions(argv);
init.line = function (logger, argv) {
var options = cli.readOptions(argv);
return init(logger, options);
};
init.completion = function () {

View File

@@ -1,19 +1,16 @@
var mout = require('mout');
var Logger = require('bower-logger');
var endpointParser = require('bower-endpoint-parser');
var Project = require('../core/Project');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function install(endpoints, options, config) {
function install(logger, endpoints, options, config) {
var project;
var decEndpoints;
var tracker;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
if (options.save === undefined) {
options.save = config.defaultSave;
}
@@ -27,22 +24,14 @@ function install(endpoints, options, config) {
});
tracker.trackDecomposedEndpoints('install', decEndpoints);
project.install(decEndpoints, options)
.done(function (installed) {
tracker.trackPackages('installed', installed);
logger.emit('end', installed);
}, function (error) {
logger.emit('error', error);
});
return logger;
return project.install(decEndpoints, options, config);
}
// -------------------
install.line = function (argv) {
install.line = function (logger, argv) {
var options = install.options(argv);
return install(options.argv.remain.slice(1), options);
return install(logger, options.argv.remain.slice(1), options);
};
install.options = function (argv) {

View File

@@ -1,21 +1,26 @@
var path = require('path');
var rimraf = require('rimraf');
var mout = require('mout');
var Q = require('q');
var Logger = require('bower-logger');
var Project = require('../core/Project');
var createLink = require('../util/createLink');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function linkSelf(config) {
var project;
var logger = new Logger();
function link(logger, name, localName) {
if (name) {
return linkTo(logger, name, localName);
} else {
return linkSelf(logger);
}
}
config = mout.object.deepFillIn(config || {}, defaultConfig);
function linkSelf(logger, config) {
var project;
config = defaultConfig(config);
project = new Project(config, logger);
project.getJson()
return project.getJson()
.then(function (json) {
var src = config.cwd;
var dst = path.join(config.storage.links, json.name);
@@ -32,69 +37,47 @@ function linkSelf(config) {
dst: dst
};
});
})
.done(function (result) {
logger.emit('end', result);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
function linkTo(name, localName, config) {
function linkTo(logger, name, localName, config) {
var src;
var dst;
var logger = new Logger();
var project;
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
project = new Project(config, logger);
localName = localName || name;
src = path.join(config.storage.links, name);
dst = path.join(process.cwd(), config.directory, localName);
// Delete destination folder if any
Q.nfcall(rimraf, dst)
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
dst: dst,
installed: installed
};
})
.done(function (result) {
logger.emit('end', result);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
// -------------------
var link = {
linkTo: linkTo,
linkSelf: linkSelf
};
link.line = function (argv) {
var options = link.options(argv);
link.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
var localName = options.argv.remain[2];
if (name) {
return linkTo(name, localName);
}
return linkSelf();
};
link.options = function (argv) {
return cli.readOptions(argv);
return link(logger, name, localName);
};
link.completion = function () {

View File

@@ -1,15 +1,13 @@
var path = require('path');
var mout = require('mout');
var Q = require('q');
var Logger = require('bower-logger');
var Project = require('../core/Project');
var semver = require('../util/semver');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function list(options, config) {
function list(logger, options, config) {
var project;
var logger = new Logger();
options = options || {};
@@ -18,13 +16,11 @@ function list(options, config) {
options.relative = true;
}
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
project = new Project(config, logger);
project.getTree()
return project.getTree(options)
.spread(function (tree, flattened) {
var baseDir = path.dirname(path.join(config.cwd, config.directory));
// Relativize paths
// Also normalize paths on windows
project.walkTree(tree, function (node) {
@@ -33,7 +29,7 @@ function list(options, config) {
}
if (options.relative) {
node.canonicalDir = path.relative(baseDir, node.canonicalDir);
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
@@ -48,7 +44,7 @@ function list(options, config) {
}
if (options.relative) {
node.canonicalDir = path.relative(baseDir, node.canonicalDir);
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
@@ -70,16 +66,7 @@ function list(options, config) {
.then(function () {
return tree;
});
})
.done(function (value) {
logger.emit('end', value);
}, function (error) {
logger.emit('error', error);
});
logger.json = !!options.paths;
return logger;
}
function checkVersions(project, tree, logger) {
@@ -87,9 +74,11 @@ function checkVersions(project, tree, logger) {
var nodes = [];
var repository = project.getPackageRepository();
// Gather all nodes
// Gather all nodes, ignoring linked nodes
project.walkTree(tree, function (node) {
nodes.push(node);
if (!node.linked) {
nodes.push(node);
}
}, true);
if (nodes.length) {
@@ -162,9 +151,9 @@ function normalize(src) {
// -------------------
list.line = function (argv) {
list.line = function (logger, argv) {
var options = list.options(argv);
return list(options);
return list(logger, options);
};
list.options = function (argv) {

View File

@@ -1,20 +1,17 @@
var mout = require('mout');
var Q = require('q');
var Logger = require('bower-logger');
var RegistryClient = require('bower-registry-client');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function lookup(name, config) {
function lookup(logger, name, config) {
var registryClient;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
config.cache = config.storage.registry;
registryClient = new RegistryClient(config, logger);
Q.nfcall(registryClient.lookup.bind(registryClient), name)
return Q.nfcall(registryClient.lookup.bind(registryClient), name)
.then(function (entry) {
// TODO: Handle entry.type.. for now it's only 'alias'
// When we got published packages, this needs to be adjusted
@@ -22,31 +19,20 @@ function lookup(name, config) {
name: name,
url: entry && entry.url
};
})
.done(function (result) {
logger.emit('end', result);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
// -------------------
lookup.line = function (argv) {
var options = lookup.options(argv);
lookup.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
if (!name) {
return null;
return new Q();
} else {
return lookup(logger, name);
}
return lookup(name);
};
lookup.options = function (argv) {
return cli.readOptions(argv);
};
lookup.completion = function () {

View File

@@ -1,39 +1,31 @@
var mout = require('mout');
var Logger = require('bower-logger');
var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function prune(config) {
function prune(logger, options, config) {
var project;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
options = options || {};
config = defaultConfig(config);
project = new Project(config, logger);
clean(project)
.done(function (removed) {
logger.emit('end', removed);
}, function (error) {
logger.emit('error', error);
});
return logger;
return clean(project, options);
}
function clean(project, removed) {
function clean(project, options, removed) {
removed = removed || {};
// Continually call clean until there is no more extraneous
// dependencies to remove
return project.getTree()
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)
return project.uninstall(names, options)
.then(function (uninstalled) {
// Are we done?
if (!mout.object.size(uninstalled)) {
@@ -42,19 +34,22 @@ function clean(project, removed) {
// Not yet, recurse!
mout.object.mixIn(removed, uninstalled);
return clean(project, removed);
return clean(project, options, removed);
});
});
}
// -------------------
prune.line = function () {
return prune();
prune.line = function (logger, argv) {
var options = prune.options(argv);
return prune(logger, options);
};
prune.options = function (argv) {
return cli.readOptions(argv);
return cli.readOptions({
'production': { type: Boolean, shorthand: 'p' },
}, argv);
};
prune.completion = function () {

View File

@@ -2,7 +2,6 @@ var mout = require('mout');
var Q = require('q');
var chalk = require('chalk');
var PackageRepository = require('../core/PackageRepository');
var Logger = require('bower-logger');
var Config = require('bower-config');
var Tracker = require('../util/analytics').Tracker;
var cli = require('../util/cli');
@@ -10,14 +9,13 @@ var createError = require('../util/createError');
var defaultConfig = require('../config');
var GitHubResolver = require('../core/resolvers/GitHubResolver');
function register(name, url, config) {
function register(logger, name, url, config) {
var repository;
var registryClient;
var tracker;
var logger = new Logger();
var force;
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
force = config.force;
tracker = new Tracker(config);
@@ -28,11 +26,11 @@ function register(name, url, config) {
// Trim name
name = name.trim();
process.nextTick(function () {
return Q.try(function () {
// Verify name
// TODO: Verify with the new spec regexp?
if (!name) {
return logger.emit('error', createError('Please type a name', 'EINVNAME'));
throw createError('Please type a name', 'EINVNAME');
}
// The public registry only allows git:// endpoints
@@ -41,7 +39,7 @@ function register(name, url, config) {
url = convertUrl(url, logger);
if (!mout.string.startsWith(url, 'git://')) {
return logger.emit('error', createError('The registry only accepts URLs starting with git://', 'EINVFORMAT'));
throw createError('The registry only accepts URLs starting with git://', 'EINVFORMAT');
}
}
@@ -50,50 +48,42 @@ function register(name, url, config) {
// Attempt to resolve the package referenced by the URL to ensure
// everything is ok before registering
repository = new PackageRepository(config, logger);
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');
}
return repository.fetch({ name: name, source: url, target: '*' });
})
.spread(function (canonicalDir, pkgMeta) {
if (pkgMeta.private) {
throw createError('The package you are trying to register is marked as private', 'EPRIV');
}
// If 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
});
})
.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);
})
.done(function (result) {
tracker.track('registered');
logger.emit('end', result);
}, function (error) {
logger.emit('error', error);
// Confirm if the user really wants to register
return Q.nfcall(logger.prompt.bind(logger), {
type: 'confirm',
message: 'Registering a package will make it installable via the registry (' +
chalk.cyan.underline(config.registry.register) + '), continue?',
default: true
});
});
})
.then(function (result) {
// If user response was negative, abort
if (!result) {
return;
}
return logger;
// Register
registryClient = repository.getRegistryClient();
logger.action('register', url, {
name: name,
url: url
});
return Q.nfcall(registryClient.register.bind(registryClient), name, url);
});
}
function convertUrl(url, logger) {
@@ -114,20 +104,16 @@ function convertUrl(url, logger) {
// -------------------
register.line = function (argv) {
var options = register.options(argv);
register.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
var url = options.argv.remain[2];
if (!name || !url) {
return null;
return new Q();
} else {
return register(logger, name, url);
}
return register(name, url);
};
register.options = function (argv) {
return cli.readOptions(argv);
};
register.completion = function () {

View File

@@ -1,51 +1,35 @@
var mout = require('mout');
var Q = require('q');
var Logger = require('bower-logger');
var RegistryClient = require('bower-registry-client');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function search(name, config) {
function search(logger, name, config) {
var registryClient;
var promise;
var tracker;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
config.cache = config.storage.registry;
registryClient = new RegistryClient(config, logger);
tracker = new Tracker(config);
tracker.track('search', name);
// If no name was specified, list all packages
if (!name) {
promise = Q.nfcall(registryClient.list.bind(registryClient));
return Q.nfcall(registryClient.list.bind(registryClient));
// Otherwise search it
} else {
promise = Q.nfcall(registryClient.search.bind(registryClient), name);
return Q.nfcall(registryClient.search.bind(registryClient), name);
}
promise
.done(function (results) {
tracker.track('searched', name);
logger.emit('end', results);
}, function (error) {
logger.emit('error', error);
});
return logger;
}
// -------------------
search.line = function (argv) {
var options = search.options(argv);
return search(options.argv.remain.slice(1).join(' '), options);
};
search.options = function (argv) {
return cli.readOptions(argv);
search.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain.slice(1).join(' ');
return search(logger, name, options);
};
search.completion = function () {

View File

@@ -1,24 +1,22 @@
var mout = require('mout');
var Logger = require('bower-logger');
var Q = require('q');
var Project = require('../core/Project');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function uninstall(names, options, config) {
function uninstall(logger, names, options, config) {
var project;
var tracker;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
project = new Project(config, logger);
tracker = new Tracker(config);
tracker.trackNames('uninstall', names);
project.getTree()
return project.getTree(options)
.spread(function (tree, flattened) {
// Uninstall nodes
return project.uninstall(names, options)
@@ -37,15 +35,7 @@ function uninstall(names, options, config) {
// Clean them!
return clean(project, children, uninstalled);
});
})
.done(function (uninstalled) {
logger.emit('end', uninstalled);
tracker.trackNames('uninstalled', Object.keys(uninstalled));
}, function (error) {
logger.emit('error', error);
});
return logger;
}
function clean(project, names, removed) {
@@ -54,6 +44,7 @@ function clean(project, names, removed) {
return project.getTree()
.spread(function (tree, flattened) {
var nodes = [];
var dependantsCounter = {};
// Grab the nodes of each specified name
mout.object.forOwn(flattened, function (node) {
@@ -62,9 +53,18 @@ function clean(project, names, removed) {
}
});
// Filter out those that have dependants
// Walk the down the tree, gathering dependants of the packages
project.walkTree(tree, function (node, nodeName) {
if (names.indexOf(nodeName) !== -1) {
dependantsCounter[nodeName] = dependantsCounter[nodeName] || 0;
dependantsCounter[nodeName] += node.nrDependants;
}
}, true);
// Filter out those that have no dependants
nodes = nodes.filter(function (node) {
return !node.nrDependants;
return !dependantsCounter[node.endpoint.name];
});
// Are we done?
@@ -99,15 +99,15 @@ function clean(project, names, removed) {
// -------------------
uninstall.line = function (argv) {
uninstall.line = function (logger, argv) {
var options = uninstall.options(argv);
var names = options.argv.remain.slice(1);
if (!names.length) {
return null;
return new Q();
} else {
return uninstall(logger, names, options);
}
return uninstall(names, options);
};
uninstall.options = function (argv) {

View File

@@ -1,15 +1,12 @@
var mout = require('mout');
var Logger = require('bower-logger');
var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function update(names, options, config) {
function update(logger, names, options, config) {
var project;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
project = new Project(config, logger);
// If names is an empty array, null them
@@ -17,21 +14,15 @@ function update(names, options, config) {
names = null;
}
project.update(names, options)
.done(function (installed) {
logger.emit('end', installed);
}, function (error) {
logger.emit('error', error);
});
return logger;
return project.update(names, options);
}
// -------------------
update.line = function (argv) {
update.line = function (logger, argv) {
var options = update.options(argv);
return update(options.argv.remain.slice(1), options);
var names = options.argv.remain.slice(1);
return update(logger, names, options);
};
update.options = function (argv) {

View File

@@ -1,6 +1,4 @@
var mout = require('mout');
var semver = require('semver');
var Logger = require('bower-logger');
var which = require('which');
var fs = require('fs');
var path = require('path');
@@ -11,21 +9,13 @@ var cli = require('../util/cli');
var defaultConfig = require('../config');
var createError = require('../util/createError');
function version(versionArg, options, config) {
function version(logger, versionArg, options, config) {
var project;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config = defaultConfig(config);
project = new Project(config, logger);
bump(project, versionArg, options.message)
.done(function () {
logger.emit('end');
}, function (error) {
logger.emit('error', error);
});
return logger;
return bump(project, versionArg, options.message);
}
function bump(project, versionArg, message) {
@@ -109,22 +99,23 @@ function filterModifiedStatusLines(stdout) {
}
function gitCommitAndTag(newVersion, message) {
message = message || 'v' + newVersion;
var tag = 'v' + newVersion;
message = message || tag;
message = message.replace(/%s/g, newVersion);
return Q.nfcall(execFile, 'git', ['add', 'bower.json'], {env: process.env})
.then(function () {
return Q.nfcall(execFile, 'git', ['commit', '-m', message], {env: process.env});
})
.then(function () {
return Q.nfcall(execFile, 'git', ['tag', newVersion, '-am', message], {env: process.env});
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {env: process.env});
});
}
// -------------------
version.line = function (argv) {
version.line = function (logger, argv) {
var options = version.options(argv);
return version(options.argv.remain[1], options);
return version(logger, options.argv.remain[1], options);
};
version.options = function (argv) {
@@ -137,4 +128,4 @@ version.completion = function () {
// TODO:
};
module.exports = version;
module.exports = version;

View File

@@ -1,32 +1,55 @@
var tty = require('tty');
var mout = require('mout');
var config = require('bower-config').read();
var object = require('mout').object;
var bowerConfig = require('bower-config');
var cli = require('./util/cli');
// Delete the json attribute because it is no longer supported
// and conflicts with --json
delete config.json;
var cachedConfigs = {};
// If interactive is auto (null), guess its value
if (config.interactive == null) {
config.interactive = process.bin === 'bower' && tty.isatty(1);
function defaultConfig(config) {
config = config || {};
var cachedConfig = readCachedConfig(config.cwd || process.cwd());
return object.merge(cachedConfig, config);
}
// If `analytics` hasn't been explicitly set, we disable
// it when ran programatically.
if (config.analytics == null) {
config.analytics = config.interactive;
function readCachedConfig(cwd) {
if (cachedConfigs[cwd]) {
return cachedConfigs[cwd];
}
var config = cachedConfigs[cwd] = bowerConfig.read(cwd);
// Delete the json attribute because it is no longer supported
// and conflicts with --json
delete config.json;
// If interactive is auto (null), guess its value
if (config.interactive == null) {
config.interactive = (
process.bin === 'bower' &&
tty.isatty(1) &&
!process.env.CI
);
}
// Merge common CLI options into the config
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;
}
// Merge common CLI options into the config
mout.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' }
}));
function resetCache () {
cachedConfigs = {};
}
module.exports = config;
module.exports = defaultConfig;
module.exports.reset = resetCache;

View File

@@ -109,6 +109,40 @@ Manager.prototype.resolve = function () {
}.bind(this));
};
Manager.prototype.preinstall = function (json) {
var that = this;
var componentsDir = path.join(this._config.cwd, this._config.directory);
// If nothing to install, skip the code bellow
if (mout.lang.isEmpty(that._dissected)) {
return Q.resolve({});
}
return Q.nfcall(mkdirp, componentsDir)
.then(function () {
return scripts.preinstall(
that._config, that._logger, that._dissected, that._installed, json
);
});
};
Manager.prototype.postinstall = function (json) {
var that = this;
var componentsDir = path.join(this._config.cwd, this._config.directory);
// If nothing to install, skip the code bellow
if (mout.lang.isEmpty(that._dissected)) {
return Q.resolve({});
}
return Q.nfcall(mkdirp, componentsDir)
.then(function () {
return scripts.postinstall(
that._config, that._logger, that._dissected, that._installed, json
);
});
};
Manager.prototype.install = function (json) {
var componentsDir;
var that = this;
@@ -125,9 +159,6 @@ Manager.prototype.install = function (json) {
componentsDir = path.join(this._config.cwd, this._config.directory);
return Q.nfcall(mkdirp, componentsDir)
.then(function () {
return scripts.preinstall(that._config, that._logger, that._dissected, that._installed, json);
})
.then(function () {
var promises = [];
@@ -169,9 +200,6 @@ Manager.prototype.install = function (json) {
return Q.all(promises);
})
.then(function () {
return scripts.postinstall(that._config, that._logger, that._dissected, that._installed, json);
})
.then(function () {
// Sync up dissected dependencies and dependants
// See: https://github.com/bower/bower/issues/879
@@ -543,13 +571,11 @@ Manager.prototype._dissect = function () {
return;
}
this._logger.info('resolution', 'Removed unnecessary ' + name + '#' + resolution + ' resolution', {
this._logger.warn('extra-resolution', 'Unnecessary resolution: ' + name + '#' + resolution, {
name: name,
resolution: resolution,
action: 'delete'
});
delete this._resolutions[name];
}, this);
// Filter only packages that need to be installed
@@ -558,12 +584,8 @@ Manager.prototype._dissect = function () {
var installedMeta = this._installed[name];
var dst;
// Analyse a few props
if (installedMeta &&
installedMeta._target === decEndpoint.target &&
installedMeta._originalSource === decEndpoint.source &&
installedMeta._release === decEndpoint.pkgMeta._release
) {
// Skip linked dependencies
if (decEndpoint.linked) {
return false;
}
@@ -573,6 +595,15 @@ Manager.prototype._dissect = function () {
return false;
}
// Analyse a few props
if (installedMeta &&
installedMeta._target === decEndpoint.target &&
installedMeta._originalSource === decEndpoint.source &&
installedMeta._release === decEndpoint.pkgMeta._release
) {
return this._config.force;
}
return true;
}, this);
}.bind(this))
@@ -791,10 +822,28 @@ Manager.prototype._storeResolution = function (pick) {
this._resolutions[name] = resolution;
};
/**
* Checks if some endpoint is compatible with already resolved target.
*
* It is used in two situations:
* * checks if resolved component matches dependency constraint
* * checks if not resolved component matches alredy fetched component
*
* If candidate matches already resolved component, it won't be downloaded.
*
* @param {Endpoint} candidate endpoint
* @param {Endpoint} resolved endpoint
*
* @return {Boolean}
*/
Manager.prototype._areCompatible = function (candidate, resolved) {
var resolvedVersion;
var highestCandidate;
var highestResolved;
var candidateIsRange = semver.validRange(candidate.target);
var resolvedIsRange = semver.validRange(resolved.target);
var candidateIsVersion = semver.valid(candidate.target);
var resolvedIsVersion = semver.valid(resolved.target);
// Check if targets are equal
if (candidate.target === resolved.target) {
@@ -802,30 +851,77 @@ Manager.prototype._areCompatible = function (candidate, resolved) {
}
resolvedVersion = resolved.pkgMeta && resolved.pkgMeta.version;
// If there is no pkgMeta, resolvedVersion is downloading now
// Check based on target requirements
if (!resolvedVersion) {
// If one of the targets is range and other is version,
// check version against the range
if (candidateIsVersion && resolvedIsRange) {
return semver.satisfies(candidate.target, resolved.target);
}
if (resolvedIsVersion && candidateIsRange) {
return semver.satisfies(resolved.target, candidate.target);
}
if (resolvedIsVersion && candidateIsVersion) {
return semver.eq(resolved.target, candidate.target);
}
// If both targets are range, check that both have same
// higher cap
if (resolvedIsRange && candidateIsRange) {
highestCandidate =
this._getCap(semver.toComparators(candidate.target), 'highest');
highestResolved =
this._getCap(semver.toComparators(resolved.target), 'highest');
// This never happens, but you can't be sure without tests
if (!highestResolved.version || !highestCandidate.version) {
return false;
}
return semver.eq(highestCandidate.version, highestResolved.version) &&
highestCandidate.comparator === highestResolved.comparator;
}
return false;
}
// If target is a version, compare against the resolved version
if (semver.valid(candidate.target)) {
if (candidateIsVersion) {
return semver.eq(candidate.target, resolvedVersion);
}
// If target is a range, check if the max versions of the range are the same
// and if the resolved version satisfies the candidate target
if (semver.validRange(candidate.target) && semver.validRange(resolved.target)) {
highestCandidate = this._getCap(semver.toComparators(candidate.target), 'highest');
highestResolved = this._getCap(semver.toComparators(resolved.target), 'highest');
return highestCandidate.version && highestResolved.version &&
semver.eq(highestCandidate.version, highestResolved.version) &&
highestCandidate.comparator === highestResolved.comparator &&
semver.satisfies(resolvedVersion, candidate.target);
// If target is a range, check if resolved version satisfies it
if (candidateIsRange) {
return semver.satisfies(resolvedVersion, candidate.target);
}
return false;
};
/**
* Gets highest/lowest version from set of comparators.
*
* The only thing that matters for this function is version number.
* Returned comparator is splitted to comparator and version parts.
*
* It is used to receive lowest / highest bound of toComparators result:
* semver.toComparators('~0.1.1') // => [ [ '>=0.1.1-0', '<0.2.0-0' ] ]
*
* Examples:
*
* _getCap([['>=2.1.1-0', '<2.2.0-0'], '<3.2.0'], 'highest')
* // => { comparator: '<', version: '3.2.0' }
*
* _getCap([['>=2.1.1-0', '<2.2.0-0'], '<3.2.0'], 'lowest')
* // => { comparator: '>=', version: '2.1.1-0' }
*
* @param {Array.<Array|string>} comparators
* @param {string} side, 'highest' (default) or 'lowest'
*
* @return {{ comparator: string, version: string }}
*/
Manager.prototype._getCap = function (comparators, side) {
var matches;
var candidate;
@@ -861,6 +957,23 @@ Manager.prototype._getCap = function (comparators, side) {
return cap;
};
/**
* Filters out unique endpoints, comparing by name and then source.
*
* It leaves last matching endpoint.
*
* Examples:
*
* manager._uniquify([
* { name: 'foo', source: 'google.com' },
* { name: 'foo', source: 'facebook.com' }
* ]);
* // => { name: 'foo', source: 'facebook.com' }
*
* @param {Array.<Endpoint>} decEndpoints
* @return {Array.<Endpoint>} Filtered elements of decEndpoints
*
*/
Manager.prototype._uniquify = function (decEndpoints) {
var length = decEndpoints.length;

View File

@@ -46,7 +46,11 @@ PackageRepository.prototype.fetch = function (decEndpoint) {
info.resolver = resolver;
isTargetable = resolver.constructor.isTargetable;
// If force flag is used, bypass cache
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);
@@ -165,19 +169,25 @@ PackageRepository.clearRuntimeCache = function () {
// ---------------------
PackageRepository.prototype._resolve = function (resolver, logger) {
var that = this;
// Resolve the resolver
return resolver.resolve()
// Store in the cache
.then(function (canonicalDir) {
return this._resolveCache.store(canonicalDir, resolver.getPkgMeta());
}.bind(this))
if (!resolver.isCacheable()) {
return canonicalDir;
}
return that._resolveCache.store(canonicalDir, resolver.getPkgMeta());
})
// Resolve promise with canonical dir and package meta
.then(function (dir) {
var pkgMeta = resolver.getPkgMeta();
logger.info('resolved', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
return [dir, pkgMeta, resolver.constructor.isTargetable()];
}.bind(this));
});
};
PackageRepository.prototype._extendLog = function (log, info) {

View File

@@ -20,7 +20,7 @@ function Project(config, logger) {
// on config and logger
// The reason behind it is that users can likely use this component
// directly if commands do not fulfil their needs
this._config = config || defaultConfig;
this._config = defaultConfig(config);
this._logger = logger || new Logger();
this._manager = new Manager(this._config, this._logger);
@@ -29,7 +29,7 @@ function Project(config, logger) {
// -----------------
Project.prototype.install = function (decEndpoints, options) {
Project.prototype.install = function (decEndpoints, options, config) {
var that = this;
var targets = [];
var resolved = {};
@@ -41,27 +41,28 @@ Project.prototype.install = function (decEndpoints, options) {
}
this._options = options || {};
this._config = config || {};
this._working = true;
// Analyse the project
return this._analyse()
.spread(function (json, tree) {
// It shows an error when issuing `bower install`
// and no bower.json is present in current directory
if(!that._jsonFile && decEndpoints.length === 0 ) {
throw createError('No bower.json present', 'ENOENT');
}
// Recover tree
that.walkTree(tree, function (node, name) {
if (node.missing || node.different) {
targets.push(node);
} else if (node.incompatible) {
if (node.incompatible) {
incompatibles.push(node);
} else if (node.missing || node.different || that._config.force) {
targets.push(node);
} else {
resolved[name] = node;
}
// Ignore linked dependencies because it's too complex to parse them
// Note that this might change in the future: #673
if (node.linked) {
return false;
}
});
}, true);
// Add decomposed endpoints as targets
decEndpoints = decEndpoints || [];
@@ -77,6 +78,12 @@ Project.prototype.install = function (decEndpoints, options) {
// Bootstrap the process
return that._bootstrap(targets, resolved, incompatibles);
})
.then(function () {
return that._manager.preinstall(that._json);
})
.then(function () {
return that._manager.install(that._json);
})
.then(function (installed) {
// Handle save and saveDev options
if (that._options.save || that._options.saveDev) {
@@ -99,7 +106,9 @@ Project.prototype.install = function (decEndpoints, options) {
// Save JSON, might contain changes to dependencies and resolutions
return that.saveJson()
.then(function () {
return installed;
return that._manager.postinstall(that._json).then(function () {
return installed;
});
});
})
.fin(function () {
@@ -129,13 +138,14 @@ Project.prototype.update = function (names, options) {
if (!names) {
// Mark each root dependency as targets
that.walkTree(tree, function (node) {
// Ignore linked extraneous because
// we don't know their real sources
if (node.extraneous && node.linked) {
return false;
// We don't know the real source of linked packages
// Instead we read its dependencies
if (node.linked) {
targets.push.apply(targets, mout.object.values(node.dependencies));
} else {
targets.push(node);
}
targets.push(node);
return false;
}, true);
// Otherwise, selectively update the specified ones
@@ -152,14 +162,15 @@ Project.prototype.update = function (names, options) {
// Add packages whose names are specified to be updated
that.walkTree(tree, function (node, name) {
// Ignore linked extraneous because
// we don't know their real source
if (node.extraneous && node.linked) {
return false;
}
if (names.indexOf(name) !== -1) {
targets.push(node);
// We don't know the real source of linked packages
// Instead we read its dependencies
if (node.linked) {
targets.push.apply(targets, mout.object.values(node.dependencies));
} else {
targets.push(node);
}
return false;
}
}, true);
@@ -173,22 +184,24 @@ Project.prototype.update = function (names, options) {
} else {
resolved[name] = node;
}
// Ignore linked dependencies because it's too complex to parse them
// Note that this might change in the future: #673
if (node.linked) {
return false;
}
}, true);
}
// Bootstrap the process
return that._bootstrap(targets, resolved, incompatibles)
.then(function () {
return that._manager.preinstall(that._json);
})
.then(function () {
return that._manager.install(that._json);
})
.then(function (installed) {
// Save JSON, might contain changes to resolutions
return that.saveJson()
.then(function () {
return installed;
return that._manager.postinstall(that._json).then(function () {
return installed;
});
});
});
})
@@ -307,7 +320,9 @@ Project.prototype.uninstall = function (names, options) {
});
};
Project.prototype.getTree = function () {
Project.prototype.getTree = function (options) {
this._options = options || {};
return this._analyse()
.spread(function (json, tree, flattened) {
var extraneous = [];
@@ -362,7 +377,7 @@ Project.prototype.walkTree = function (node, fn, onlyOnce) {
while (queue.length) {
node = queue.shift();
result = fn(node, node.name);
result = fn(node, node.endpoint ? node.endpoint.name : node.name);
// Abort traversal if result is false
if (result === false) {
@@ -449,7 +464,6 @@ Project.prototype._analyse = function () {
.spread(function (json, installed, links) {
var root;
var jsonCopy = mout.lang.deepClone(json);
var flattened = mout.object.mixIn({}, installed, links);
root = {
name: json.name,
@@ -460,6 +474,8 @@ Project.prototype._analyse = function () {
root: true
};
mout.object.mixIn(installed, links);
// Mix direct extraneous as dependencies
// (dependencies installed without --save/--save-dev)
jsonCopy.dependencies = jsonCopy.dependencies || {};
@@ -469,34 +485,40 @@ Project.prototype._analyse = function () {
var isSaved = jsonCopy.dependencies[key] || jsonCopy.devDependencies[key];
// The _direct propery is saved by the manager when .newly is specified
if (!isSaved && pkgMeta._direct) {
// It may happen pkgMeta is undefined if package is uninstalled
if (!isSaved && pkgMeta && pkgMeta._direct) {
decEndpoint.extraneous = true;
jsonCopy.dependencies[key] = (pkgMeta._originalSource || pkgMeta._source) + '#' + pkgMeta._target;
if (decEndpoint.linked) {
jsonCopy.dependencies[key] = pkgMeta.version || '*';
} else {
jsonCopy.dependencies[key] = (pkgMeta._originalSource || pkgMeta._source) + '#' + pkgMeta._target;
}
}
});
// Restore the original dependencies cross-references,
// that is, the parent-child relationships
this._restoreNode(root, flattened, 'dependencies');
this._restoreNode(root, installed, 'dependencies');
// Do the same for the dev dependencies
if (!this._options.production) {
this._restoreNode(root, flattened, 'devDependencies');
this._restoreNode(root, installed, 'devDependencies');
}
// Restore the rest of the extraneous (not installed directly)
mout.object.forOwn(flattened, function (decEndpoint, name) {
mout.object.forOwn(installed, function (decEndpoint, name) {
if (!decEndpoint.dependants) {
decEndpoint.extraneous = true;
this._restoreNode(decEndpoint, flattened, 'dependencies');
this._restoreNode(decEndpoint, installed, 'dependencies');
// Note that it has no dependants, just dependencies!
root.dependencies[name] = decEndpoint;
}
}, this);
// Remove root from the flattened tree
delete flattened[json.name];
delete installed[json.name];
return [json, root, flattened];
return [json, root, installed];
}.bind(this));
};
@@ -523,9 +545,7 @@ Project.prototype._bootstrap = function (targets, resolved, incompatibles) {
if (!mout.object.size(this._json.resolutions)) {
delete this._json.resolutions;
}
}.bind(this))
// Install resolved ones
.then(this._manager.install.bind(this._manager, this._json));
}.bind(this));
};
Project.prototype._readJson = function () {
@@ -687,7 +707,7 @@ Project.prototype._removePackages = function (packages) {
// Delete directory
if (!dir) {
promise = Q.resolve();
that._logger.warn('not-installed', name, {
that._logger.warn('not-installed', '\'' + name + '\'' + ' cannot be uninstalled as it is not currently installed', {
name: name
});
} else {
@@ -715,7 +735,7 @@ Project.prototype._removePackages = function (packages) {
promises.push(promise);
});
return Q.all(promises);
})
@@ -730,7 +750,7 @@ Project.prototype._removePackages = function (packages) {
});
};
Project.prototype._restoreNode = function (node, flattened, jsonKey) {
Project.prototype._restoreNode = function (node, flattened, jsonKey, processed) {
var deps;
// Do not restore if the node is missing
@@ -740,10 +760,11 @@ Project.prototype._restoreNode = function (node, flattened, jsonKey) {
node.dependencies = node.dependencies || {};
node.dependants = node.dependants || {};
processed = processed || {};
// Only process deps that are yet processed
// Only process deps that are not yet processed
deps = mout.object.filter(node.pkgMeta[jsonKey], function (value, key) {
return !node.dependencies[key];
return !processed[node.name + ':' + key];
});
mout.object.forOwn(deps, function (value, key) {
@@ -790,15 +811,17 @@ Project.prototype._restoreNode = function (node, flattened, jsonKey) {
// Cross reference
node.dependencies[key] = restored;
processed[node.name + ':' + key] = true;
restored.dependants = restored.dependants || {};
restored.dependants[node.name] = node;
restored.dependants[node.name] = mout.object.mixIn({}, node); // We need to clone due to shared objects in the manager!
// Call restore for this dependency
this._restoreNode(restored, flattened, 'dependencies');
this._restoreNode(restored, flattened, 'dependencies', processed);
// Do the same for the incompatible local package
if (local && restored !== local) {
this._restoreNode(local, flattened, 'dependencies');
this._restoreNode(local, flattened, 'dependencies', processed);
}
}, this);
};

View File

@@ -5,6 +5,7 @@ var Q = require('q');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var LRU = require('lru-cache');
var lockFile = require('lockfile');
var semver = require('../util/semver');
var readJson = require('../util/readJson');
var copy = require('../util/copy');
@@ -18,6 +19,9 @@ function ResolveCache(config) {
// - etc..
this._config = config;
this._dir = this._config.storage.packages;
this._lockDir = this._config.storage.packages;
mkdirp.sync(this._lockDir);
// Cache is stored/retrieved statically to ensure singularity
// among instances
@@ -97,6 +101,7 @@ ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
var sourceId;
var release;
var dir;
var pkgLock;
var promise;
var that = this;
@@ -107,36 +112,41 @@ ResolveCache.prototype.store = function (canonicalDir, 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 directory exists
// Check if destination directory exists to prevent issuing lock at all times
return Q.nfcall(fs.stat, dir)
.then(function () {
// If it does exists, remove it
return Q.nfcall(rimraf, dir);
}, function (err) {
// If directory does not exists, ensure its basename
// is created
if (err.code === 'ENOENT') {
return Q.nfcall(mkdirp, path.dirname(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;
}
throw err;
})
// Move the canonical to sourceId/target
.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)
.then(function () {
return Q.nfcall(rimraf, canonicalDir);
return copy.copyDir(canonicalDir, dir);
});
});
});
}).finally(function () {
lockFile.unlockSync(pkgLock);
});
}).finally(function () {
// Ensure no tmp dir is left on disk.
return Q.nfcall(rimraf, canonicalDir);
});
})
.then(function () {

View File

@@ -35,6 +35,9 @@ function GitHubResolver(decEndpoint, config, logger) {
if (this._config.proxy || this._config.httpsProxy) {
this._source = this._source.replace('git://', 'https://');
}
// Enable shallow clones for GitHub repos
this._shallowClone = true;
}
util.inherits(GitHubResolver, GitRemoteResolver);

View File

@@ -25,6 +25,9 @@ function GitRemoteResolver(decEndpoint, config, logger) {
} else {
this._host = url.parse(this._source).host;
}
// Disable shallow clones
this._shallowClone = false;
}
util.inherits(GitRemoteResolver, GitResolver);
@@ -113,7 +116,7 @@ GitRemoteResolver.prototype._fastClone = function (resolution) {
args = ['clone', this._source, '-b', branch, '--progress', '.'];
// If the host does not support shallow clones, we don't use --depth=1
if (!GitRemoteResolver._noShallow.get(this._host)) {
if (this._shallowClone && !GitRemoteResolver._noShallow.get(this._host)) {
args.push('--depth', 1);
}

View File

@@ -10,7 +10,6 @@ var mout = require('mout');
var Resolver = require('./Resolver');
var semver = require('../../util/semver');
var createError = require('../../util/createError');
var defaultConfig = require('../../config');
var hasGit;
@@ -22,13 +21,13 @@ try {
hasGit = false;
}
// Set template dir to the empty directory so that user templates are not run
// This environment variable is not multiple config aware but it's not documented
// anyway
mkdirp.sync(defaultConfig.storage.empty);
process.env.GIT_TEMPLATE_DIR = defaultConfig.storage.empty;
function GitResolver(decEndpoint, config, logger) {
// Set template dir to the empty directory so that user templates are not run
// This environment variable is not multiple config aware but it's not documented
// anyway
mkdirp.sync(config.storage.empty);
process.env.GIT_TEMPLATE_DIR = config.storage.empty;
Resolver.call(this, decEndpoint, config, logger);
if (!hasGit) {
@@ -165,6 +164,18 @@ GitResolver.prototype._findResolution = function (target) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
if ((/^[a-f0-9]{4,40}$/).test(target)) {
if (target.length < 12) {
that._logger.warn(
'short-sha',
'Consider using longer commit SHA to avoid conflicts'
);
}
that._resolution = { type: 'commit', commit: target };
return that._resolution;
}
branches = Object.keys(branches);
tags = Object.keys(tags);

View File

@@ -114,6 +114,26 @@ Resolver.prototype.resolve = function () {
});
};
Resolver.prototype.isCacheable = function () {
// Bypass cache for local dependencies
if (this._source &&
/^(?:file:[\/\\]{2}|[A-Z]:)?\.?\.?[\/\\]/.test(this._source)
) {
return false;
}
// We don't want to cache moving targets like branches
if (this._pkgMeta &&
this._pkgMeta._resolution &&
this._pkgMeta._resolution.type === 'branch')
{
return false;
}
return true;
};
// -----------------
// Abstract functions that must be implemented by concrete resolvers
@@ -149,8 +169,8 @@ Resolver.prototype._createTempDir = function () {
});
}.bind(this))
.then(function (dir) {
this._tempDir = dir;
return dir;
// nfcall may return multiple callback arguments as an array
return this._tempDir = Array.isArray(dir) ? dir[0] : dir;
}.bind(this));
};
@@ -201,26 +221,36 @@ Resolver.prototype._applyPkgMeta = function (meta) {
}
// Otherwise remove them from the temp dir
return removeIgnores(this._tempDir, meta.ignore)
return removeIgnores(this._tempDir, meta)
.then(function () {
return meta;
});
};
Resolver.prototype._savePkgMeta = function (meta) {
var that = this;
var contents;
// Store original source & target
meta._source = this._source;
meta._target = this._target;
['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 this._pkgMeta = meta;
}.bind(this));
return that._pkgMeta = meta;
});
};
module.exports = Resolver;

View File

@@ -1,7 +1,5 @@
var util = require('util');
var path = require('path');
var Q = require('q');
var rimraf = require('rimraf');
var which = require('which');
var LRU = require('lru-cache');
var mout = require('mout');
@@ -67,19 +65,13 @@ SvnResolver.prototype._resolve = function () {
return this._findResolution()
.then(function () {
return that._checkout()
// Always run cleanup after checkout to ensure that .svn 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 that._export();
});
};
// -----------------
SvnResolver.prototype._checkout = function () {
SvnResolver.prototype._export = function () {
var promise;
var timer;
var reporter;
@@ -88,19 +80,19 @@ SvnResolver.prototype._checkout = function () {
this.source = SvnResolver.getSource(this._source);
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
this._logger.action('export', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
if (resolution.type === 'commit') {
promise = cmd('svn', ['checkout', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
promise = cmd('svn', ['export', '--force', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
promise = cmd('svn', ['checkout', this._source + '/trunk', this._tempDir]);
promise = cmd('svn', ['export', '--force', this._source + '/trunk', this._tempDir]);
} else if (resolution.type === 'branch') {
promise = cmd('svn', ['checkout', this._source + '/branches/' + resolution.branch, this._tempDir]);
promise = cmd('svn', ['export', '--force', this._source + '/branches/' + resolution.branch, this._tempDir]);
} else {
promise = cmd('svn', ['checkout', this._source + '/tags/' + resolution.tag, this._tempDir]);
promise = cmd('svn', ['export', '--force', this._source + '/tags/' + resolution.tag, this._tempDir]);
}
// Throttle the progress reporter to 1 time each sec
@@ -232,12 +224,6 @@ SvnResolver.prototype._findResolution = function (target) {
});
};
SvnResolver.prototype._cleanup = function () {
var svnFolder = path.join(this._tempDir, '.svn');
return Q.nfcall(rimraf, svnFolder);
};
SvnResolver.prototype._savePkgMeta = function (meta) {
var version;
@@ -340,25 +326,11 @@ SvnResolver.tags = function (source) {
value = cmd('svn', ['list', source + '/tags', '--verbose'])
.spread(function (stout) {
var tags = {};
var lines = stout.toString()
.trim()
.split(/[\r\n]+/);
// For each line in the refs, match only the tags
lines.forEach(function (line) {
var match = line.match(/\s+([0-9]+)\s.+\s([a-z0-9.]+)\//i);
if (match && match[2] !== '.') {
tags[match[2]] = match[1];
}
});
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
this._cache.tags.set(source, tags);
return tags;
}.bind(this));
// Store the promise to be reused until it resolves
@@ -379,28 +351,14 @@ SvnResolver.branches = function (source) {
value = cmd('svn', ['list', source + '/branches', '--verbose'])
.spread(function (stout) {
var branches = SvnResolver.parseSubversionListOutput(stout.toString());
// trunk is a branch!
var branches = {
'trunk': '*'
};
var lines = stout.toString()
.trim()
.split(/[\r\n]+/);
// For each line in the refs, match only the banches
lines.forEach(function (line) {
var match = line.match(/\s+([0-9]+)\s.+\s([a-z0-9.]+)\//i);
if (match && match[2] !== '.') {
branches[match[2]] = match[1];
}
});
branches.trunk = '*';
this._cache.branches.set(source, branches);
return branches;
}.bind(this));
// Store the promise to be reused until it resolves
@@ -410,6 +368,25 @@ SvnResolver.branches = function (source) {
return value;
};
SvnResolver.parseSubversionListOutput = function (stout) {
var entries = {};
var lines = stout
.trim()
.split(/[\r\n]+/);
// For each line in the refs, match only the branches
lines.forEach(function (line) {
var match = line.match(/\s+([0-9]+)\s.+\s([\w.$-]+)\//i);
if (match && match[2] !== '.') {
entries[match[2]] = match[1];
}
});
return entries;
};
SvnResolver.clearRuntimeCache = function () {
// Reset cache for branches, tags, etc
mout.object.forOwn(SvnResolver._cache, function (lru) {

View File

@@ -1,11 +1,12 @@
var abbrev = require('abbrev');
var mout = require('mout');
var commands = require('./commands');
var PackageRepository = require('./core/PackageRepository');
var pkg = require('../package.json');
var abbreviations = abbrev(expandNames(commands));
abbreviations.i = 'install';
abbreviations.rm = 'uninstall';
abbreviations.unlink = 'uninstall';
abbreviations.ls = 'list';
function expandNames(obj, prefix, stack) {
@@ -29,12 +30,14 @@ function clearRuntimeCache() {
// Note that in edge cases, some architecture components instance's
// in-memory cache might be skipped.
// If that's a problem, you should create and fresh instances instead.
var PackageRepository = require('./core/PackageRepository');
PackageRepository.clearRuntimeCache();
}
module.exports = {
version: pkg.version,
commands: commands,
config: require('./config'),
config: require('./config')(),
abbreviations: abbreviations,
reset: clearRuntimeCache
};

View File

@@ -121,8 +121,6 @@ JsonRenderer.prototype.prompt = function (prompts) {
});
};
JsonRenderer.prototype.updateNotice = function () {};
// -------------------------
JsonRenderer.prototype._stringify = function (log) {

View File

@@ -1,10 +1,8 @@
var cardinal = require('cardinal');
var chalk = require('chalk');
var path = require('path');
var mout = require('mout');
var archy = require('archy');
var Q = require('q');
var inquirer = require('inquirer');
var stringifyObject = require('stringify-object');
var os = require('os');
var pkg = require(path.join(__dirname, '../..', 'package.json'));
@@ -32,6 +30,16 @@ function StandardRenderer(command, config) {
} else {
this._compact = process.stdout.columns < 120;
}
var exitOnPipeError = function (err) {
if (err.code === 'EPIPE') {
process.exit(0);
}
};
// It happens when piping command to "head" util
process.stdout.on('error', exitOnPipeError);
process.stderr.on('error', exitOnPipeError);
}
StandardRenderer.prototype.end = function (data) {
@@ -106,16 +114,12 @@ StandardRenderer.prototype.prompt = function (prompts) {
// Prompt
deferred = Q.defer();
var inquirer = require('inquirer');
inquirer.prompt(prompts, deferred.resolve);
return deferred.promise;
};
StandardRenderer.prototype.updateNotice = function (data) {
var str = template.render('std/update-notice.std', data);
this._write(process.stderr, str);
};
// -------------------------
StandardRenderer.prototype._help = function (data) {
@@ -171,10 +175,14 @@ StandardRenderer.prototype._update = function (packages) {
StandardRenderer.prototype._list = function (tree) {
var cliTree;
tree.root = true;
cliTree = this._tree2archy(tree);
if (tree.pkgMeta) {
tree.root = true;
cliTree = archy(this._tree2archy(tree));
} else {
cliTree = stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
}
this._write(process.stdout, archy(cliTree));
this._write(process.stdout, cliTree);
};
StandardRenderer.prototype._search = function (results) {
@@ -221,6 +229,11 @@ StandardRenderer.prototype._link = function (data) {
level: 'info',
message: data.dst + ' > ' + data.src
});
// Print also a tree of the installed packages
if (data.installed) {
this._install(data.installed);
}
};
StandardRenderer.prototype._register = function (data) {
@@ -389,6 +402,8 @@ StandardRenderer.prototype._write = function (stream, str) {
};
StandardRenderer.prototype._highlightJson = function (json) {
var cardinal = require('cardinal');
return cardinal.highlight(stringifyObject(json, { indent: ' ' }), {
theme: {
String: {
@@ -467,6 +482,7 @@ StandardRenderer.prototype._tree2archy = function (node) {
StandardRenderer._wideCommands = [
'install',
'update',
'link',
'info',
'home',
'register'

View File

@@ -1,27 +1,55 @@
var Q = require('q');
var Insight = require('insight');
var mout = require('mout');
var config = require('../config');
var pkg = require('../../package.json');
var analytics = module.exports;
var insight;
// Insight takes long to load, and often causes problems
// in non-interactive environment, so we load it lazily
function ensureInsight () {
if (!insight) {
var Insight = require('insight');
var pkg = require('../../package.json');
insight = new Insight({
trackingCode: 'UA-43531210-1',
packageName: pkg.name,
packageVersion: pkg.version
});
}
}
// Initializes the application-wide insight singleton and asks for the
// permission on the CLI during the first run.
analytics.setup = function setup() {
analytics.setup = function setup (config) {
var deferred = Q.defer();
insight = new Insight({
trackingCode: 'UA-43531210-1',
packageName: pkg.name,
packageVersion: pkg.version
});
// Display the ask prompt only if it hasn't been answered before
// and the current session is interactive.
if (insight.optOut === undefined && config.interactive) {
insight.askPermission(null, deferred.resolve);
// if `analytics` hasn't been explicitly set
if (config.analytics == null) {
ensureInsight();
// if there is a stored value
if (insight.optOut !== undefined) {
// set analytics to the stored value
config.analytics = !insight.optOut;
deferred.resolve();
} else {
if (config.interactive) {
insight.askPermission(null, function(err, optIn) {
// optIn callback param was exactly opposite before 0.4.3
// so we force at least insight@0.4.3 in package.json
config.analytics = optIn;
deferred.resolve();
});
} else {
// no specified value, no stored value, and can't prompt for one
// so set analytics to true
config.analytics = true;
deferred.resolve();
}
}
} else {
// use the specified value
deferred.resolve();
}
@@ -35,9 +63,8 @@ var Tracker = analytics.Tracker = function Tracker(config) {
};
Tracker.prototype.track = function track() {
if (!insight) {
throw new Error('You must call analytics.setup() prior to tracking.');
}
ensureInsight();
insight.track.apply(insight, arguments);
};

View File

@@ -8,9 +8,12 @@ var createError = require('./createError');
// The concurrency limit here is kind of magic. You don't really gain a lot from
// having a large number of commands spawned at once, so it isn't super
// important for this number to be large. However, it would still be nice to
// *know* how high this number can be, rather than having to guess low.
var throttler = new PThrottler(50);
// important for this number to be large. Reports have shown that much more than 5
// or 10 cause issues for corporate networks, private repos or situations where
// internet bandwidth is limited. We're running with a concurrency of 5 until
// 1.4.X is released, at which time we'll move to what was discussed in #1262
// https://github.com/bower/bower/pull/1262
var throttler = new PThrottler(5);
var winBatchExtensions;
var winWhichCache;

View File

@@ -2,7 +2,7 @@ var path = require('path');
var fs = require('graceful-fs');
var zlib = require('zlib');
var DecompressZip = require('decompress-zip');
var tar = require('tar');
var tar = require('tar-fs');
var Q = require('q');
var mout = require('mout');
var junk = require('junk');
@@ -23,6 +23,7 @@ extractors = {
'.gz': extractGz,
'application/zip': extractZip,
'application/x-zip': extractZip,
'application/x-zip-compressed': extractZip,
'application/x-tar': extractTar,
'application/x-tgz': extractTarGz,
'application/x-gzip': extractGz
@@ -50,13 +51,11 @@ function extractTar(archive, dst) {
fs.createReadStream(archive)
.on('error', deferred.reject)
.pipe(tar.Extract({
path: dst,
follow: false, // Do not follow symlinks (#699)
filter: filterSymlinks // Filter symlink files
.pipe(tar.extract(dst, {
ignore: isSymlink // Filter symlink files
}))
.on('error', deferred.reject)
.on('close', deferred.resolve.bind(deferred, dst));
.on('finish', deferred.resolve.bind(deferred, dst));
return deferred.promise;
}
@@ -68,13 +67,11 @@ function extractTarGz(archive, dst) {
.on('error', deferred.reject)
.pipe(zlib.createGunzip())
.on('error', deferred.reject)
.pipe(tar.Extract({
path: dst,
follow: false, // Do not follow symlinks (#699)
filter: filterSymlinks // Filter symlink files
.pipe(tar.extract(dst, {
ignore: isSymlink // Filter symlink files
}))
.on('error', deferred.reject)
.on('close', deferred.resolve.bind(deferred, dst));
.on('finish', deferred.resolve.bind(deferred, dst));
return deferred.promise;
}
@@ -93,6 +90,10 @@ function extractGz(archive, dst) {
return deferred.promise;
}
function isSymlink(entry) {
return entry.type === 'SymbolicLink';
}
function filterSymlinks(entry) {
return entry.type !== 'SymbolicLink';
}

View File

@@ -4,19 +4,26 @@ var fstreamIgnore = require('fstream-ignore');
var mout = require('mout');
var Q = require('q');
function removeIgnores(dir, ignore) {
function removeIgnores(dir, meta) {
var reader;
var applyIgnores;
var deferred = Q.defer();
var ignored = [];
var nonIgnored = [];
var nonIgnored = ['bower.json'];
// Don't ignore main files
nonIgnored = nonIgnored.concat(meta.main || []);
nonIgnored = nonIgnored.map(function (file) {
return path.join(dir, file);
});
reader = fstreamIgnore({
path: dir,
type: 'Directory'
});
reader.addIgnoreRules(ignore);
reader.addIgnoreRules(meta.ignore || []);
// Monkey patch applyIgnores such that we get hold of all ignored files
applyIgnores = reader.applyIgnores;

View File

@@ -1,7 +1,7 @@
{
"name": "bower",
"version": "1.3.0",
"description": "The browser package manager.",
"version": "1.3.11",
"description": "The browser package manager",
"author": "Twitter",
"licenses": [
{
@@ -9,10 +9,7 @@
"url": "https://github.com/bower/bower/blob/master/LICENSE"
}
],
"repository": {
"type": "git",
"url": "git://github.com/bower/bower.git"
},
"repository": "bower/bower",
"main": "lib",
"homepage": "http://bower.io",
"engines": {
@@ -21,57 +18,61 @@
"dependencies": {
"abbrev": "~1.0.4",
"archy": "0.0.2",
"bower-config": "~0.5.0",
"bower-endpoint-parser": "~0.2.0",
"bower-config": "~0.5.2",
"bower-endpoint-parser": "~0.2.2",
"bower-json": "~0.4.0",
"bower-logger": "~0.2.2",
"bower-registry-client": "~0.1.4",
"bower-registry-client": "~0.2.0",
"cardinal": "~0.4.0",
"chalk": "~0.4.0",
"chalk": "~0.5.0",
"chmodr": "~0.1.0",
"decompress-zip": "~0.0.3",
"fstream": "~0.1.22",
"fstream-ignore": "~0.0.6",
"glob": "~3.2.1",
"graceful-fs": "~2.0.0",
"handlebars": "~1.3.0",
"inquirer": "~0.4.0",
"junk": "~0.2.0",
"mkdirp": "~0.3.5",
"mout": "~0.9.0",
"nopt": "~2.1.1",
"decompress-zip": "0.0.8",
"fstream": "~1.0.2",
"fstream-ignore": "~1.0.1",
"glob": "~4.0.2",
"graceful-fs": "~3.0.1",
"handlebars": "~2.0.0",
"inquirer": "~0.7.1",
"insight": "~0.4.3",
"is-root": "~1.0.0",
"junk": "~1.0.0",
"lockfile": "~1.0.0",
"lru-cache": "~2.5.0",
"open": "~0.0.3",
"osenv": "~0.0.3",
"mkdirp": "~0.5.0",
"mout": "~0.10.0",
"nopt": "~3.0.0",
"opn": "~1.0.0",
"osenv": "~0.1.0",
"p-throttler": "0.1.0",
"promptly": "~0.2.0",
"q": "~1.0.0",
"request": "~2.33.0",
"q": "~1.0.1",
"request": "~2.42.0",
"request-progress": "~0.3.0",
"retry": "~0.6.0",
"rimraf": "~2.2.0",
"semver": "~2.2.1",
"stringify-object": "~0.2.0",
"tar": "~0.1.17",
"tmp": "~0.0.20",
"update-notifier": "~0.1.3",
"which": "~1.0.5",
"p-throttler": "~0.0.1",
"insight": "~0.3.0",
"is-root": "~0.1.0",
"shell-quote": "~1.4.1"
"semver": "~2.3.0",
"shell-quote": "~1.4.1",
"stringify-object": "~1.0.0",
"tar-fs": "~0.5.0",
"tmp": "0.0.23",
"update-notifier": "~0.2.0",
"which": "~1.0.5"
},
"devDependencies": {
"expect.js": "~0.2.0",
"grunt": "~0.4.1",
"grunt-simple-mocha": "~0.4.0",
"grunt-contrib-watch": "~0.5.0",
"grunt-contrib-jshint": "~0.8.0",
"coveralls": "~2.11.0",
"expect.js": "~0.3.1",
"grunt": "~0.4.4",
"grunt-cli": "^0.1.13",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-exec": "~0.4.2",
"mocha": "*",
"nock": "~0.27.2",
"istanbul": "~0.2.4",
"proxyquire": "~0.5.0",
"load-grunt-tasks": "~0.3.0"
"grunt-simple-mocha": "~0.4.0",
"istanbul": "~0.3.2",
"load-grunt-tasks": "~0.6.0",
"mocha": "~1.21.4",
"nock": "~0.46.0",
"node-uuid": "~1.4.1",
"proxyquire": "~1.0.1"
},
"scripts": {
"test": "grunt test"

View File

@@ -1,6 +1,6 @@
{
"command": "link",
"description": "The link functionality allows developers to easily test their packages.\nLinking is a two-step process.\n\nUsing 'bower link' in a project folder will create a global link.\nThen, in some other package, 'bower link <name>' will create a link in the components folder pointing to the previously created link.\n\nThis allows to easily test a package because changes will be reflected immediately.\nPlease note that bower will not fetch the linked package dependencies.\n\nBower will overwrite the link when installing/updating.",
"description": "The link functionality allows developers to easily test their packages.\nLinking is a two-step process.\n\nUsing 'bower link' in a project folder will create a global link.\nThen, in some other package, 'bower link <name>' will create a link in the components folder pointing to the previously created link.\n\nThis allows to easily test a package because changes will be reflected immediately.\nWhen the link is no longer necessary, simply remove it with 'bower uninstall <name>'.",
"usage": [
"link [<options>]",
"link <name> [<local name>] [<options>]"

View File

@@ -13,12 +13,12 @@
{
"shorthand": "-S",
"flag": "--save",
"description": "Save installed packages into the project's bower.json dependencies"
"description": "Remove uninstalled packages from the project's bower.json dependencies"
},
{
"shorthand": "-D",
"flag": "--save-dev",
"description": "Save installed packages into the project's bower.json devDependencies"
"description": "Remove uninstalled packages from the project's bower.json devDependencies"
}
]
}

View File

@@ -10,7 +10,7 @@
"init": "Interactively create a bower.json file",
"install": "Install a package locally",
"link": "Symlink a package folder",
"list": "List local packages",
"list": "List local packages - and possible updates",
"lookup": "Look up a package URL by name",
"prune": "Removes local extraneous packages",
"register": "Register a package",
@@ -58,6 +58,10 @@
{
"flag": "--allow-root",
"description": "Allows running commands as root"
},
{
"flag": "--version",
"description": "Output Bower version"
}
]
}

View File

@@ -1,9 +1,9 @@
{{#yellow}}Please note that,{{/yellow}}
{{#condense}}
{{#each picks}}
{{#if dependants}}{{#white}}{{dependants}}{{/white}}{{else}}none{{/if}} depend on {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#white}}{{pkgMeta._release}}{{/white}}{{/if}}
{{#if dependants}}{{#green}}{{dependants}}{{/green}}{{else}}none{{/if}} depends on {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#green}}{{endpoint.name}}#{{pkgMeta._release}}{{/green}}{{/if}}
{{/each}}
{{/condense}}
Resort to using {{#cyan}}{{suitable.endpoint.name}}#{{resolution}}{{/cyan}} which resolved to {{#white}}{{suitable.pkgMeta._release}}{{/white}}
Resort to using {{#cyan}}{{suitable.endpoint.name}}#{{resolution}}{{/cyan}} which resolved to {{#green}}{{suitable.endpoint.name}}#{{suitable.pkgMeta._release}}{{/green}}
Code incompatibilities may occur.

View File

@@ -1,9 +1,9 @@
{{#yellow}}Unable to find a suitable version for {{name}}, please choose one:{{/yellow}}
{{#condense}}
{{#each picks}}
{{#magenta}}{{sum @index 1}}){{/magenta}} {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#white}}{{pkgMeta._release}}{{/white}}{{/if}}{{#if dependants}} and is required by {{#white}}{{dependants}}{{/white}} {{/if}}
{{#magenta}}{{sum @index 1}}){{/magenta}} {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#green}}{{pkgMeta._release}}{{/green}}{{/if}}{{#if dependants}} and is required by {{#green}}{{dependants}}{{/green}} {{/if}}
{{/each}}
{{/condense}}
{{#unless saveResolutions}}
Prefix the choice with ! to persist it to bower.json
{{/unless}}
{{/unless}}

View File

@@ -1,5 +0,0 @@
{{#red}}-----------------------------------------{{/red}}
Update available: {{#yellow}}{{latest}}{{/yellow}} {{#cyan}}(current: {{current}}){{/cyan}}
Run {{#yellow}}npm update -g {{name}}{{/yellow}} to update
{{#red}}-----------------------------------------{{/red}}

View File

@@ -1,51 +1,51 @@
[
{
"canonicalDir": "/test/assets/temp-resolve-cache/3668e6529b32a6d3e8931a68474e909d/0.2.0",
"canonicalDir": "/test/tmp/temp-resolve-cache/3668e6529b32a6d3e8931a68474e909d/0.2.0",
"pkgMeta": {
"name": "abc",
"version": "0.2.0"
}
},
{
"canonicalDir": "/test/assets/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/0.0.1",
"canonicalDir": "/test/tmp/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/0.0.1",
"pkgMeta": {
"name": "foo",
"version": "0.0.1"
}
},
{
"canonicalDir": "/test/assets/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/0.1.0",
"canonicalDir": "/test/tmp/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/0.1.0",
"pkgMeta": {
"name": "foo",
"version": "0.1.0"
}
},
{
"canonicalDir": "/test/assets/temp-resolve-cache/3668e6529b32a6d3e8931a68474e909d/0.2.1",
"canonicalDir": "/test/tmp/temp-resolve-cache/3668e6529b32a6d3e8931a68474e909d/0.2.1",
"pkgMeta": {
"name": "foo",
"version": "0.2.1"
}
},
{
"canonicalDir": "/test/assets/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/aa",
"canonicalDir": "/test/tmp/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/aa",
"pkgMeta": {
"name": "foo",
"_target": "aa"
}
},
{
"canonicalDir": "/test/assets/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/bar",
"canonicalDir": "/test/tmp/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/bar",
"pkgMeta": {
"name": "foo",
"_target": "bar"
}
},
{
"canonicalDir": "/test/assets/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/foo",
"canonicalDir": "/test/tmp/temp-resolve-cache/77008abea14f06f199c2f481362b48d9/foo",
"pkgMeta": {
"name": "foo",
"_target": "foo"
}
}
]
]

View File

@@ -4,7 +4,7 @@ var Logger = require('bower-logger');
var Resolver = require('../../../lib/core/resolvers/Resolver');
var defaultConfig = require('../../../lib/config');
var resolver = new Resolver({ source: 'foo' }, defaultConfig, new Logger());
var resolver = new Resolver({ source: 'foo' }, defaultConfig(), new Logger());
resolver._createTempDir()
.then(function (dir) {
// Need to write something to prevent tmp to automatically

View File

@@ -4,7 +4,7 @@ var Logger = require('bower-logger');
var Resolver = require('../../../lib/core/resolvers/Resolver');
var defaultConfig = require('../../../lib/config');
var resolver = new Resolver({ source: 'foo' }, defaultConfig, new Logger());
var resolver = new Resolver({ source: 'foo' }, defaultConfig(), new Logger());
resolver._createTempDir()
.then(function (dir) {
// Need to write something to prevent tmp to automatically

6
test/commands/index.js Normal file
View File

@@ -0,0 +1,6 @@
describe('integration tests', function () {
require('./init');
require('./install');
require('./uninstall');
require('./update');
});

50
test/commands/init.js Normal file
View File

@@ -0,0 +1,50 @@
var path = require('path');
var expect = require('expect.js');
var fs = require('fs');
var helpers = require('../helpers');
var bower = helpers.require('lib/index');
describe('bower init', function () {
var tempDir = new helpers.TempDir();
var bowerJsonPath = path.join(tempDir.path, 'bower.json');
var config = {
cwd: tempDir.path,
interactive: true
};
it('generates bower.json file', function () {
tempDir.prepare();
var logger = bower.commands.init(config);
return helpers.expectEvent(logger, 'prompt')
.spread(function (prompt, answer) {
answer({
name: 'test-name',
version: 'test-version',
description: 'test-description',
moduleType: 'test-moduleType',
keywords: 'test-keyword',
authors: 'test-author',
license: 'test-license',
homepage: 'test-homepage',
private: true
});
return helpers.expectEvent(logger, 'prompt');
})
.spread(function (prompt, answer) {
answer({
prompt: true
});
return helpers.expectEvent(logger, 'end');
})
.then(function () {
expect(fs.existsSync(bowerJsonPath)).to.be(true);
});
});
});

205
test/commands/install.js Normal file
View File

@@ -0,0 +1,205 @@
var expect = require('expect.js');
var object = require('mout').object;
var helpers = require('../helpers');
var commands = helpers.require('lib/index').commands;
describe('bower install', function () {
var tempDir = new helpers.TempDir();
var package = new helpers.TempDir({
'bower.json': {
name: 'package'
}
}).prepare();
var gitPackage = new helpers.TempDir();
var installLogger = function(packages, options, config) {
config = object.merge(config || {}, {
cwd: tempDir.path
});
return commands.install(packages, options, config);
};
var install = function(packages, options, config) {
var logger = installLogger(packages, options, config);
return helpers.expectEvent(logger, 'end');
};
it('writes to bower.json if --save flag is used', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test'
}
});
return install([package.path], { save: true }).then(function() {
expect(tempDir.read('bower.json')).to.contain('dependencies');
});
});
it('reads .bowerrc from cwd', function () {
package.prepare({ foo: 'bar' });
tempDir.prepare({
'.bowerrc': { directory: 'assets' },
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
}
});
return install().then(function() {
expect(tempDir.read('assets/package/foo')).to.be('bar');
});
});
it('runs preinstall hook', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
},
'.bowerrc': {
scripts: {
preinstall: 'bash -c "echo -n % > preinstall.txt"'
}
}
});
return install().then(function() {
expect(tempDir.read('preinstall.txt')).to.be('package');
});
});
it('runs preinstall hook', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "echo -n % > postinstall.txt"'
}
}
});
return install().then(function() {
expect(tempDir.read('postinstall.txt')).to.be('package');
});
});
// To be discussed, but that's the implementation now
it('does not run hooks if nothing is installed', function () {
tempDir.prepare({
'bower.json': {
name: 'test'
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "echo -n % > hooks.txt"',
preinstall: 'bash -c "echo -n % > hooks.txt"'
}
}
});
return install().then(function() {
expect(tempDir.exists('hooks.txt')).to.be(false);
});
});
it('runs postinstall after bower.json is written', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test'
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "cat bower.json > hook.txt"',
}
}
});
return install([package.path], { save: true }).then(function() {
expect(tempDir.read('hook.txt')).to.contain('dependencies');
});
});
it('display the output of hook scripts', function (next) {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "echo foobar"'
}
}
});
var lastAction = null;
installLogger().intercept(function (log) {
if (log.level === 'action') {
lastAction = log;
}
}).on('end', function () {
expect(lastAction.message).to.be('foobar');
next();
});
});
it('works for git repositories', function () {
return gitPackage.prepareGit({
'1.0.0': {
'bower.json': {
name: 'package'
},
'version.txt': '1.0.0'
},
'1.0.1': {
'bower.json': {
name: 'package'
},
'version.txt': '1.0.1'
}
}).then(function() {
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.0'
}
}
});
return install().then(function() {
expect(tempDir.read('bower_components/package/version.txt')).to.contain('1.0.0');
});
});
});
});

View File

@@ -0,0 +1,52 @@
var path = require('path');
var expect = require('expect.js');
var fs = require('fs');
var helpers = require('../helpers');
var bower = helpers.require('lib/index');
describe('bower uninstall', function () {
var tempDir = new helpers.TempDir({
'bower.json': {
name: 'hello-world',
dependencies: {
'underscore': '*'
}
}
});
beforeEach(function() {
tempDir.prepare();
});
var bowerJsonPath = path.join(tempDir.path, 'bower.json');
function bowerJson() {
return JSON.parse(fs.readFileSync(bowerJsonPath));
}
var config = {
cwd: tempDir.path,
interactive: true
};
it('does not remove anything from dependencies by default', function () {
var logger = bower.commands.uninstall(['underscore'], undefined, config);
return helpers.expectEvent(logger, 'end')
.then(function () {
expect(bowerJson().dependencies).to.eql({ 'underscore': '*' });
});
});
it('removes dependency from bower.json if --save flag is used', function () {
var logger = bower.commands.uninstall(['underscore'], {save: true}, config);
return helpers.expectEvent(logger, 'end')
.then(function () {
expect(bowerJson().dependencies).to.eql({});
});
});
});

239
test/commands/update.js Normal file
View File

@@ -0,0 +1,239 @@
var expect = require('expect.js');
var object = require('mout').object;
var helpers = require('../helpers');
var commands = helpers.require('lib/index').commands;
describe('bower update', function () {
var tempDir = new helpers.TempDir();
var gitPackage = new helpers.TempDir();
gitPackage.prepareGit({
'1.0.0': {
'bower.json': {
name: 'package'
},
'version.txt': '1.0.0'
},
'1.0.1': {
'bower.json': {
name: 'package'
},
'version.txt': '1.0.1'
}
});
var package = new helpers.TempDir({
'bower.json': {
name: 'package'
}
}).prepare();
var updateLogger = function(packages, options, config) {
config = object.merge(config || {}, {
cwd: tempDir.path
});
return commands.update(packages, options, config);
};
var update = function(packages, options, config) {
var logger = updateLogger(packages, options, config);
return helpers.expectEvent(logger, 'end');
};
var install = function(packages, options, config) {
config = object.merge(config || {}, {
cwd: tempDir.path
});
var logger = commands.install(
packages, options, config
);
return helpers.expectEvent(logger, 'end');
};
it('install missing packages', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
}
});
return update().then(function() {
expect(tempDir.exists('bower_components/package/bower.json')).to.equal(true);
expect(tempDir.read('bower_components/package/bower.json')).to.contain('"name": "package"');
});
});
it('runs preinstall hook when installing missing package', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
},
'.bowerrc': {
scripts: {
preinstall: 'bash -c "echo -n % > preinstall.txt"'
}
}
});
return update().then(function() {
expect(tempDir.read('preinstall.txt')).to.be('package');
});
});
it('runs postinstall hook when installing missing package', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "echo -n % > postinstall.txt"'
}
}
});
return update().then(function() {
expect(tempDir.read('postinstall.txt')).to.be('package');
});
});
it('doesn\'t runs postinstall when no package is update', function () {
package.prepare();
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: package.path
}
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "echo -n % > postinstall.txt"'
}
}
});
return install().then(function() {
tempDir.prepare();
return update().then(function() {
expect(tempDir.exists('postinstall.txt')).to.be(false);
});
});
});
it('updates a package', function () {
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.0'
}
}
});
return install().then(function() {
expect(tempDir.read('bower_components/package/version.txt')).to.contain('1.0.0');
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.1'
}
}
});
return update().then(function() {
expect(tempDir.read('bower_components/package/version.txt')).to.contain('1.0.1');
});
});
});
it('runs preinstall hook when updating a package', function () {
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.0'
}
},
'.bowerrc': {
scripts: {
preinstall: 'bash -c "echo -n % > preinstall.txt"'
}
}
});
return install().then(function() {
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.1'
}
}
});
expect(tempDir.exists('preinstall.txt')).to.be(false);
return update().then(function() {
expect(tempDir.read('preinstall.txt')).to.be('package');
});
});
});
it('runs postinstall hook when updating a package', function () {
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.0'
}
},
'.bowerrc': {
scripts: {
postinstall: 'bash -c "echo -n % > postinstall.txt"'
}
}
});
return install().then(function() {
tempDir.prepare({
'bower.json': {
name: 'test',
dependencies: {
package: gitPackage.path + '#1.0.1'
}
}
});
expect(tempDir.exists('postinstall.txt')).to.be(false);
return update().then(function() {
expect(tempDir.read('postinstall.txt')).to.be('package');
});
});
});
});

270
test/core/Manager.js Normal file
View File

@@ -0,0 +1,270 @@
var expect = require('expect.js');
var path = require('path');
var rimraf = require('rimraf');
var Logger = require('bower-logger');
var Manager = require('../../lib/core/Manager');
var defaultConfig = require('../../lib/config');
describe('Manager', function () {
var manager;
var packagesCacheDir =
path.join(__dirname, '../assets/temp-resolve-cache');
var registryCacheDir =
path.join(__dirname, '../assets/temp-registry-cache');
after(function () {
rimraf.sync(registryCacheDir);
rimraf.sync(packagesCacheDir);
});
beforeEach(function (next) {
var logger = new Logger();
var config = defaultConfig({
storage: {
packages: packagesCacheDir,
registry: registryCacheDir
}
});
manager = new Manager(config, logger);
next();
});
describe('_areCompatible', function () {
describe('resolved is being fetched', function() {
it('accepts endpoints with same targets', function () {
expect(manager._areCompatible(
{ name: 'foo', target: 'xxx' },
{ name: 'bar', target: 'xxx' }
)).to.be(true);
});
it('rejects endpoints with different targets', function () {
expect(manager._areCompatible(
{ name: 'foo', target: 'xxx' },
{ name: 'bar', target: 'yyy' }
)).to.be(false);
});
it('accepts with version and matching range', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '0.1.2' },
{ name: 'bar', target: '~0.1.0' }
)).to.be(true);
});
it('rejects with version and non-matching range', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '0.1.2' },
{ name: 'bar', target: '~0.1.3' }
)).to.be(false);
});
it('accepts with matching range and version', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~0.1.0' },
{ name: 'bar', target: '0.1.2' }
)).to.be(true);
});
it('accepts with non-matching range and version', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~0.1.3' },
{ name: 'bar', target: '0.1.2' }
)).to.be(false);
});
it('accepts with matching ranges', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~0.1.0' },
{ name: 'bar', target: '~0.1.3' }
)).to.be(true);
});
it('rejects with non-matching ranges', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~0.1.0' },
{ name: 'bar', target: '~0.2.3' }
)).to.be(false);
});
it('rejects with non-matching ranges', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~0.1.0' },
{ name: 'bar', target: 'xxx' }
)).to.be(false);
});
});
describe('resolved is already fetched', function () {
var resolved = {
name: 'foo',
target: '~1.2.1',
pkgMeta: {
version: '1.2.3'
}
};
it('accepts if the same version as resolved', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '1.2.3' },
resolved
)).to.be(true);
});
it('rejects if different version than resolved', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '1.2.4' },
resolved
)).to.be(false);
});
it('accepts if range matches resolved version', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~1.2.1' },
resolved
)).to.be(true);
});
it('rejects if range does not match', function () {
expect(manager._areCompatible(
{ name: 'foo', target: '~1.2.4' },
resolved
)).to.be(false);
});
});
});
describe('_getCap', function () {
it('finds highest bound', function () {
var highest = manager._getCap(
[['2.1.1-0', '<2.2.0-0'], '<3.2.0'],
'highest'
);
expect(highest).to.eql({
version: '3.2.0',
comparator: '<'
});
});
it('finds lowest bound', function () {
var highest = manager._getCap(
[['2.1.1-0', '<2.2.0-0'], '<3.2.0'],
'lowest'
);
expect(highest).to.eql({
version: '2.1.1-0',
comparator: ''
});
});
it('defaults to highest bound', function () {
var highest = manager._getCap(
['1.0.0', '2.0.0']
);
expect(highest).to.eql({
version: '2.0.0',
comparator: ''
});
});
it('ignores non-semver elements', function () {
var highest = manager._getCap(
['0.9', '>1.0.1', ['<1.0.0', 'lol']]
);
expect(highest).to.eql({
version: '1.0.1',
comparator: '>'
});
});
it('returns empty object if cap is not found', function () {
var highest = manager._getCap(
['0.9'] // Not a semver
);
expect(highest).to.eql({});
});
});
describe('_uniquify', function () {
it('leaves last unique element', function () {
var unique = manager._uniquify([
{ name: 'foo', id: 1 },
{ name: 'foo', id: 2 }
]);
expect(unique).to.eql([
{ name: 'foo', id: 2 }
]);
});
it('compares by name first', function () {
var unique = manager._uniquify([
{ name: 'foo', source: 'google.com' },
{ name: 'foo', source: 'facebook.com' }
]);
expect(unique).to.eql([
{ name: 'foo', source: 'facebook.com' }
]);
});
it('compares by source if name is not available', function () {
var unique = manager._uniquify([
{ source: 'facebook.com' },
{ source: 'facebook.com' }
]);
expect(unique).to.eql([
{ source: 'facebook.com' }
]);
});
it('leaves different targets intact', function() {
var unique = manager._uniquify([
{ source: 'facebook.com', target: 'a1b2c3' },
{ source: 'facebook.com', target: 'ffffff' }
]);
expect(unique).to.eql([
{ source: 'facebook.com', target: 'a1b2c3' },
{ source: 'facebook.com', target: 'ffffff' }
]);
});
it('removes if same targets', function() {
var unique = manager._uniquify([
{ source: 'facebook.com', target: 'ffffff' },
{ source: 'facebook.com', target: 'ffffff' }
]);
expect(unique).to.eql([
{ source: 'facebook.com', target: 'ffffff' }
]);
});
it('ignores other fields', function() {
var unique = manager._uniquify([
{ source: 'facebook.com', foo: 12 },
{ source: 'facebook.com', bar: 13 }
]);
expect(unique).to.eql([
{ source: 'facebook.com', bar: 13 }
]);
});
});
});

View File

@@ -18,10 +18,11 @@ describe('PackageRepository', function () {
var resolverFactoryHook;
var resolverFactoryClearHook;
var testPackage = path.resolve(__dirname, '../assets/package-a');
var tempPackage = path.resolve(__dirname, '../assets/temp');
var packagesCacheDir = path.join(__dirname, '../assets/temp-resolve-cache');
var registryCacheDir = path.join(__dirname, '../assets/temp-registry-cache');
var tempPackage = path.resolve(__dirname, '../tmp/temp-package');
var packagesCacheDir = path.join(__dirname, '../tmp/temp-resolve-cache');
var registryCacheDir = path.join(__dirname, '../tmp/temp-registry-cache');
var mockSource = 'file://' + testPackage;
var forceCaching = true;
after(function () {
rimraf.sync(registryCacheDir);
@@ -34,7 +35,7 @@ describe('PackageRepository', function () {
var logger = new Logger();
// Config
config = mout.object.deepMixIn({}, defaultConfig, {
config = defaultConfig({
storage: {
packages: packagesCacheDir,
registry: registryCacheDir
@@ -51,6 +52,14 @@ describe('PackageRepository', function () {
decEndpoint.source = mockSource;
resolver = new resolvers.GitRemote(decEndpoint, _config, _logger);
if (forceCaching) {
// Force to use cache even for local resources
resolver.isCacheable = function () {
return true;
};
}
resolverFactoryHook(resolver);
return Q.resolve(resolver);
@@ -140,7 +149,7 @@ describe('PackageRepository', function () {
});
it('should attempt to retrieve a resolved package from the resolve package', function (next) {
var called;
var called = false;
var originalRetrieve = packageRepository._resolveCache.retrieve;
packageRepository._resolveCache.retrieve = function (source) {
@@ -161,6 +170,31 @@ describe('PackageRepository', function () {
.done();
});
it('should avoid using cache for local resources', function (next) {
forceCaching = false;
var called = false;
var originalRetrieve = packageRepository._resolveCache.retrieve;
packageRepository._resolveCache.retrieve = function (source) {
called = true;
expect(source).to.be(mockSource);
return originalRetrieve.apply(this, arguments);
};
packageRepository.fetch({ name: '', source: testPackage, target: '~0.1.0' })
.spread(function (canonicalDir, pkgMeta) {
expect(called).to.be(false);
expect(fs.existsSync(canonicalDir)).to.be(true);
expect(pkgMeta).to.be.an('object');
expect(pkgMeta.name).to.be('package-a');
expect(pkgMeta.version).to.be('0.1.1');
forceCaching = true;
next();
})
.done();
});
it('should just call the resolver resolve method if no appropriate package was found in the resolve cache', function (next) {
var called = [];

View File

@@ -14,16 +14,16 @@ var md5 = require('../../lib/util/md5');
describe('ResolveCache', function () {
var resolveCache;
var testPackage = path.resolve(__dirname, '../assets/package-a');
var tempPackage = path.resolve(__dirname, '../assets/temp');
var tempPackage2 = path.resolve(__dirname, '../assets/temp2');
var cacheDir = path.join(__dirname, '../assets/temp-resolve-cache');
var tempPackage = path.resolve(__dirname, '../tmp/temp-package');
var tempPackage2 = path.resolve(__dirname, '../tmp/temp2-package');
var cacheDir = path.join(__dirname, '../tmp/temp-resolve-cache');
before(function (next) {
// Delete cache folder
rimraf.sync(cacheDir);
// Instantiate resolver cache
resolveCache = new ResolveCache(mout.object.deepMixIn(defaultConfig, {
resolveCache = new ResolveCache(defaultConfig({
storage: {
packages: cacheDir
}
@@ -55,7 +55,7 @@ describe('ResolveCache', function () {
});
function initialize(cacheDir) {
return new ResolveCache(mout.object.deepMixIn(defaultConfig, {
return new ResolveCache(defaultConfig({
storage: {
packages: cacheDir
}
@@ -138,30 +138,6 @@ describe('ResolveCache', function () {
.done();
});
it('should overwrite if the exact same package source/version exists', function (next) {
var cachePkgDir = path.join(cacheDir, md5('foo'), '1.0.0-rc.blehhh');
mkdirp.sync(cachePkgDir);
fs.writeFileSync(path.join(cachePkgDir, '_bleh'), 'w00t');
resolveCache.store(tempPackage, {
name: 'foo',
version: '1.0.0-rc.blehhh',
_source: 'foo',
_target: '*'
})
.then(function (dir) {
expect(dir).to.equal(cachePkgDir);
expect(fs.existsSync(dir)).to.be(true);
expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
expect(fs.existsSync(tempPackage)).to.be(false);
expect(fs.existsSync(path.join(cachePkgDir, '_bleh'))).to.be(false);
next();
})
.done();
});
it('should read the package meta if not present', function (next) {
var pkgMeta = path.join(tempPackage, '.bower.json');
@@ -241,14 +217,14 @@ describe('ResolveCache', function () {
resolveCache.store(tempPackage, {
name: 'foo',
_source: 'foo',
_source: 'foobar',
_target: 'some-branch'
})
.then(function (dir) {
// Ensure mock was called
expect(hittedMock).to.be(true);
expect(dir).to.equal(path.join(cacheDir, md5('foo'), 'some-branch'));
expect(dir).to.equal(path.join(cacheDir, md5('foobar'), 'some-branch'));
expect(fs.existsSync(dir)).to.be(true);
expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
expect(fs.existsSync(tempPackage)).to.be(false);
@@ -310,6 +286,30 @@ describe('ResolveCache', function () {
})
.done();
});
it('should be possible to store two package at same time', function (next) {
var store = resolveCache.store.bind(resolveCache, tempPackage, {
name: 'foo',
_source: 'foo',
_target: 'foo/bar'
});
var store2 = resolveCache.store.bind(resolveCache, tempPackage2, {
name: 'foo',
_source: 'foo',
_target: 'foo/bar'
});
Q.all([store(), store2()]).then(function (dirs) {
var dir = dirs[0];
expect(dir).to.equal(path.join(cacheDir, md5('foo'), 'foo%2Fbar'));
expect(fs.existsSync(dir)).to.be(true);
expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
expect(fs.existsSync(tempPackage)).to.be(false);
expect(fs.existsSync(tempPackage2)).to.be(false);
next();
}).done();
});
});
describe('.versions', function () {
@@ -908,7 +908,7 @@ describe('ResolveCache', function () {
expect(entries).to.be.an('array');
expectedJson = fs.readFileSync(path.join(__dirname, '../assets/resolve-cache/list-json-1.json'));
expectedJson = expectedJson.toString();
expectedJson = expectedJson.toString().trim();
mout.object.forOwn(entries, function (entry) {
// Trim absolute bower path from json

View File

@@ -14,9 +14,9 @@ var defaultConfig = require('../../lib/config');
describe('resolverFactory', function () {
var tempSource;
var logger = new Logger();
var registryClient = new RegistryClient(mout.object.fillIn({
cache: defaultConfig._registry
}, defaultConfig));
var registryClient = new RegistryClient(defaultConfig({
cache: defaultConfig()._registry
}));
afterEach(function (next) {
logger.removeAllListeners();
@@ -34,7 +34,7 @@ describe('resolverFactory', function () {
});
function callFactory(decEndpoint, config) {
return resolverFactory(decEndpoint, config || defaultConfig, logger, registryClient);
return resolverFactory(decEndpoint, defaultConfig(config), logger, registryClient);
}
it('should recognize git remote endpoints correctly', function (next) {
@@ -421,7 +421,7 @@ describe('resolverFactory', function () {
var endpoints;
var temp;
tempSource = path.resolve(__dirname, '../assets/tmp');
tempSource = path.resolve(__dirname, '../tmp/tmp');
mkdirp.sync(tempSource);
fs.writeFileSync(path.join(tempSource, '.git'), 'foo');
fs.writeFileSync(path.join(tempSource, 'file.with.multiple.dots'), 'foo');
@@ -431,7 +431,7 @@ describe('resolverFactory', function () {
// Absolute path to folder with .git file
endpoints[tempSource] = tempSource;
// Relative path to folder with .git file
endpoints[__dirname + '/../assets/tmp'] = tempSource;
endpoints[__dirname + '/../tmp/tmp'] = tempSource;
// Absolute path to folder
temp = path.resolve(__dirname, '../assets/test-temp-dir');
@@ -584,14 +584,12 @@ describe('resolverFactory', function () {
it('should use the configured shorthand resolver', function (next) {
callFactory({ source: 'bower/bower' })
.then(function (resolver) {
var config;
var config = {
shorthandResolver: 'git://bower.io/{{owner}}/{{package}}/{{shorthand}}'
};
expect(resolver.getSource()).to.equal('git://github.com/bower/bower.git');
config = mout.object.fillIn({
shorthandResolver: 'git://bower.io/{{owner}}/{{package}}/{{shorthand}}'
}, defaultConfig);
return callFactory({ source: 'IndigoUnited/promptly' }, config);
})
.then(function (resolver) {
@@ -616,7 +614,7 @@ describe('resolverFactory', function () {
it('should error out if there\'s no suitable resolver for a given source', function (next) {
resolverFactory({ source: 'some-package-that-will-never-exist' }, defaultConfig, logger)
resolverFactory({ source: 'some-package-that-will-never-exist' }, defaultConfig(), logger)
.then(function () {
throw new Error('Should have failed');
}, function (err) {

View File

@@ -35,12 +35,12 @@ describe('FsResolver', function () {
}
});
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new FsResolver(decEndpoint, config || defaultConfig, logger);
return new FsResolver(decEndpoint, defaultConfig(), logger);
}
describe('.constructor', function () {
@@ -82,7 +82,7 @@ describe('FsResolver', function () {
it('should resolve always to true (for now..)', function (next) {
var resolver = create(testPackage);
tempSource = path.resolve(__dirname, '../../assets/tmp');
tempSource = path.resolve(__dirname, '../../tmp/tmp');
mkdirp.sync(tempSource);
fs.writeFileSync(path.join(tempSource, '.bower.json'), JSON.stringify({
name: 'test'
@@ -161,7 +161,7 @@ describe('FsResolver', function () {
it('should rename to index if source is a folder with just one file in it', function (next) {
var resolver;
tempSource = path.resolve(__dirname, '../../assets/tmp');
tempSource = path.resolve(__dirname, '../../tmp/tmp');
mkdirp.sync(tempSource);
resolver = create(tempSource);
@@ -181,7 +181,7 @@ describe('FsResolver', function () {
it('should not rename to index if source is a folder with just bower.json/component.json file in it', function (next) {
var resolver;
tempSource = path.resolve(__dirname, '../../assets/tmp');
tempSource = path.resolve(__dirname, '../../tmp/tmp');
mkdirp.sync(tempSource);
resolver = create(tempSource);
@@ -235,7 +235,7 @@ describe('FsResolver', function () {
var mode0777;
var resolver;
tempSource = path.resolve(__dirname, '../../assets/temp');
tempSource = path.resolve(__dirname, '../../tmp/temp-source');
resolver = create(tempSource);
copy.copyFile(path.join(testPackage, 'foo'), tempSource)

View File

@@ -33,12 +33,12 @@ describe('GitFsResolver', function () {
GitFsResolver.clearRuntimeCache();
}
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new GitFsResolver(decEndpoint, config || defaultConfig, logger);
return new GitFsResolver(decEndpoint, defaultConfig(), logger);
}
describe('.constructor', function () {

View File

@@ -15,27 +15,19 @@ describe('GitHub', function () {
logger = new Logger();
});
beforeEach(function () {
// Turn off strict ssl because it gives problems with nock
defaultConfig.strictSsl = false;
});
afterEach(function () {
// Clean nocks
nock.cleanAll();
logger.removeAllListeners();
// Enable strict ssl back again
defaultConfig.strictSsl = true;
});
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new GitHubResolver(decEndpoint, config || defaultConfig, logger);
return new GitHubResolver(decEndpoint, defaultConfig({ strictSsl: false }), logger);
}
describe('.constructor', function () {

View File

@@ -21,12 +21,12 @@ describe('GitRemoteResolver', function () {
GitRemoteResolver.clearRuntimeCache();
}
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new GitRemoteResolver(decEndpoint, config || defaultConfig, logger);
return new GitRemoteResolver(decEndpoint, defaultConfig(), logger);
}
describe('.constructor', function () {

View File

@@ -13,7 +13,7 @@ var GitResolver = require('../../../lib/core/resolvers/GitResolver');
var defaultConfig = require('../../../lib/config');
describe('GitResolver', function () {
var tempDir = path.resolve(__dirname, '../../assets/tmp');
var tempDir = path.resolve(__dirname, '../../tmp/tmp');
var originalrefs = GitResolver.refs;
var logger;
@@ -30,12 +30,12 @@ describe('GitResolver', function () {
GitResolver.clearRuntimeCache();
}
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new GitResolver(decEndpoint, config || defaultConfig, logger);
return new GitResolver(decEndpoint, defaultConfig(), logger);
}
describe('misc', function () {
@@ -334,7 +334,7 @@ describe('GitResolver', function () {
}.bind(this));
};
resolver = new DummyResolver({ source: 'foo', target: 'master' }, defaultConfig, logger);
resolver = new DummyResolver({ source: 'foo', target: 'master' }, defaultConfig(), logger);
resolver.resolve()
.then(function () {
@@ -750,6 +750,27 @@ describe('GitResolver', function () {
.done();
});
it('should resolve to the specified short commit', function (next) {
var resolver;
GitResolver.refs = function () {
return Q.resolve([
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa refs/heads/master'
]);
};
resolver = create('foo');
resolver._findResolution('bbbbbbb')
.then(function (resolution) {
expect(resolution).to.eql({
type: 'commit',
commit: 'bbbbbbb'
});
next();
})
.done();
});
it('should resolve to the specified tag if it exists', function (next) {
var resolver;

View File

@@ -13,10 +13,11 @@ var Resolver = require('../../../lib/core/resolvers/Resolver');
var defaultConfig = require('../../../lib/config');
describe('Resolver', function () {
var tempDir = path.resolve(__dirname, '../../assets/tmp');
var tempDir = path.resolve(__dirname, '../../tmp/tmp');
var testPackage = path.resolve(__dirname, '../../assets/package-a');
var logger;
var dirMode0777;
var config = defaultConfig();
before(function () {
var stat;
@@ -33,12 +34,12 @@ describe('Resolver', function () {
logger.removeAllListeners();
});
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new Resolver(decEndpoint, config || defaultConfig, logger);
return new Resolver(decEndpoint, config, logger);
}
describe('.getSource', function () {
@@ -333,7 +334,7 @@ describe('Resolver', function () {
}.bind(this));
};
resolver = new DummyResolver({ source: 'foo'}, defaultConfig, logger);
resolver = new DummyResolver({ source: 'foo'}, config, logger);
resolver.resolve()
.then(function () {
@@ -461,7 +462,7 @@ describe('Resolver', function () {
osTempDir = path.resolve(tmp.tmpdir);
expect(dir.indexOf(osTempDir)).to.be(0);
expect(dir.indexOf(defaultConfig.tmp)).to.be(0);
expect(dir.indexOf(config.tmp)).to.be(0);
expect(path.basename(dirname)).to.equal('bower');
expect(path.dirname(path.dirname(dirname))).to.equal(osTempDir);
@@ -487,13 +488,13 @@ describe('Resolver', function () {
it('should remove the folder after execution', function (next) {
this.timeout(15000); // Give some time to execute
rimraf(defaultConfig.tmp, function (err) {
rimraf(config.tmp, function (err) {
if (err) return next(err);
cmd('node', ['test/assets/test-temp-dir/test.js'], { cwd: path.resolve(__dirname, '../../..') })
.then(function () {
expect(fs.existsSync(defaultConfig.tmp)).to.be(true);
expect(fs.readdirSync(defaultConfig.tmp)).to.eql([]);
expect(fs.existsSync(config.tmp)).to.be(true);
expect(fs.readdirSync(config.tmp)).to.eql([]);
next();
}, function (err) {
next(new Error(err.details));
@@ -503,15 +504,15 @@ describe('Resolver', function () {
});
it('should remove the folder on an uncaught exception', function (next) {
rimraf(defaultConfig.tmp, function (err) {
rimraf(config.tmp, function (err) {
if (err) return next(err);
cmd('node', ['test/assets/test-temp-dir/test-exception.js'], { cwd: path.resolve(__dirname, '../../..') })
.then(function () {
next(new Error('The command should have failed'));
}, function () {
expect(fs.existsSync(defaultConfig.tmp)).to.be(true);
expect(fs.readdirSync(defaultConfig.tmp)).to.eql([]);
expect(fs.existsSync(config.tmp)).to.be(true);
expect(fs.readdirSync(config.tmp)).to.eql([]);
next();
})
.done();
@@ -676,7 +677,7 @@ describe('Resolver', function () {
.done();
});
it('should remove files that match the ignore patterns', function (next) {
it('should remove files that match the ignore patterns excluding main files', function (next) {
var resolver = create({ source: 'foo', name: 'foo' });
mkdirp.sync(tempDir);
@@ -701,6 +702,8 @@ describe('Resolver', function () {
expect(fs.existsSync(path.join(tempDir, 'foo'))).to.be(true);
expect(fs.existsSync(path.join(tempDir, 'baz'))).to.be(true);
expect(fs.existsSync(path.join(tempDir, 'test'))).to.be(false);
expect(fs.existsSync(path.join(tempDir, 'bower.json'))).to.be(true);
expect(fs.existsSync(path.join(tempDir, 'main.js'))).to.be(true);
expect(fs.existsSync(path.join(tempDir, 'more/docs'))).to.be(false);
expect(fs.existsSync(path.join(tempDir, 'more/assets'))).to.be(false);
next();
@@ -784,6 +787,32 @@ describe('Resolver', function () {
})
.done();
});
it('should warn user for missing attributes in bower.json', function (next) {
var resolver = create('fooooo');
resolver._tempDir = tempDir;
var notifiedCount = 0;
logger.on('log', function (log) {
notifiedCount ++;
expect(log).to.be.an('object');
expect(log.level).to.be('warn');
if (notifiedCount === 1) {
expect(log.message).to.contain('bar is missing "main" entry in bower.json');
} else {
expect(log.message).to.contain('bar is missing "ignore" entry in bower.json');
}
});
resolver._savePkgMeta({ name: 'bar' });
expect(notifiedCount).to.be(2);
resolver._savePkgMeta({ name: 'bar', main: 'foo' });
expect(notifiedCount).to.be(3);
// should not warn again
resolver._savePkgMeta({ name: 'bar', main: 'flart', ignore: 'blat' });
expect(notifiedCount).to.be(3);
next();
});
});
describe('#isTargetable', function () {
@@ -803,5 +832,43 @@ describe('Resolver', function () {
})
.done();
});
});
describe('#isCacheable', function () {
it('caches for normal name', function () {
var resolver = new Resolver({ source: 'foo' });
expect(resolver.isCacheable()).to.be(true);
});
it('does not cache for absolute paths', function () {
var resolver = new Resolver({ source: '/foo' });
expect(resolver.isCacheable()).to.be(false);
});
it('does not cache for relative paths', function () {
var resolver = new Resolver({ source: './foo' });
expect(resolver.isCacheable()).to.be(false);
});
it('does not cache for parent paths', function () {
var resolver = new Resolver({ source: '../foo' });
expect(resolver.isCacheable()).to.be(false);
});
it('does not cache for file:/// prefix', function () {
var resolver = new Resolver({ source: 'file:///foo' });
expect(resolver.isCacheable()).to.be(false);
});
it('does not cache for windows paths', function () {
var resolver = new Resolver({ source: '..\\foo' });
expect(resolver.isCacheable()).to.be(false);
});
it('does not cache for windows absolute paths', function () {
var resolver = new Resolver({ source: 'C:\\foo' });
expect(resolver.isCacheable()).to.be(false);
});
});
});

View File

@@ -2,18 +2,16 @@ var expect = require('expect.js');
var util = require('util');
var path = require('path');
var fs = require('graceful-fs');
var chmodr = require('chmodr');
var rimraf = require('rimraf');
var mkdirp = require('mkdirp');
var Q = require('q');
var mout = require('mout');
var Logger = require('bower-logger');
var copy = require('../../../lib/util/copy');
var SvnResolver = require('../../../lib/core/resolvers/SvnResolver');
var defaultConfig = require('../../../lib/config');
describe('SvnResolver', function () {
var tempDir = path.resolve(__dirname, '../../assets/tmp');
var tempDir = path.resolve(__dirname, '../../tmp/tmp');
var testPackage = path.resolve(__dirname, '../../assets/package-svn/repo');
var testPackageAdmin = path.resolve(__dirname, '../../assets/package-svn/admin');
var originaltags = SvnResolver.tags;
@@ -32,12 +30,12 @@ describe('SvnResolver', function () {
SvnResolver.clearRuntimeCache();
}
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new SvnResolver(decEndpoint, config || defaultConfig, logger);
return new SvnResolver(decEndpoint, defaultConfig(), logger);
}
describe('misc', function () {
@@ -271,35 +269,24 @@ describe('SvnResolver', function () {
}.bind(this));
};
DummyResolver.prototype._checkout = function () {
this._stack.push('before _checkout');
DummyResolver.prototype._export = function () {
this._stack.push('before _export');
return Q.resolve()
.then(function (val) {
this._stack.push('after _checkout');
this._stack.push('after _export');
return val;
}.bind(this));
};
DummyResolver.prototype._cleanup = function () {
this._stack.push('before _cleanup');
return SvnResolver.prototype._cleanup.apply(this, arguments)
.then(function (val) {
this._stack.push('after _cleanup');
return val;
}.bind(this));
};
resolver = new DummyResolver({ source: 'foo', target: '1.0.0' }, defaultConfig, logger);
resolver = new DummyResolver({ source: 'foo', target: '1.0.0' }, defaultConfig(), logger);
resolver.resolve()
.then(function () {
expect(resolver.getStack()).to.eql([
'before _findResolution',
'after _findResolution',
'before _checkout',
'after _checkout',
'before _cleanup',
'after _cleanup'
'before _export',
'after _export'
]);
next();
})
@@ -608,87 +595,6 @@ describe('SvnResolver', function () {
});
});
describe('._cleanup', function () {
beforeEach(function () {
mkdirp.sync(tempDir);
});
afterEach(function (next) {
clearResolverRuntimeCache();
// Need to chmodr before removing..at least on windows
// because .svn has some read only files
chmodr(tempDir, 0777, function () {
rimraf(tempDir, next);
});
});
it('should remove the .svn folder from the temp dir', function (next) {
var resolver = create('foo');
var dst = path.join(tempDir, '.svn');
this.timeout(30000); // Give some time to copy
// Copy .svn folder to the tempDir
copy.copyDir(path.resolve(__dirname, '../../assets/package-svn/repo/.svn'), dst, {
mode: 0777
})
.then(function () {
resolver._tempDir = tempDir;
return resolver._cleanup()
.then(function () {
expect(fs.existsSync(dst)).to.be(false);
next();
});
})
.done();
});
it('should not fail if .svn does not exist for some reason', function (next) {
var resolver = create('foo');
var dst = path.join(tempDir, '.svn');
resolver._tempDir = tempDir;
resolver._cleanup()
.then(function () {
expect(fs.existsSync(dst)).to.be(false);
next();
})
.done();
});
it('should sill run even if _checkout fails for some reason', function (next) {
var resolver = create('foo');
var called = false;
SvnResolver.tags = function () {
return Q.resolve({
'1.0.0': 1
});
};
resolver._tempDir = tempDir;
resolver._checkout = function () {
return Q.reject(new Error('Some error'));
};
resolver._cleanup = function () {
called = true;
return SvnResolver.prototype._cleanup.apply(this, arguments);
};
resolver.resolve()
.then(function () {
next(new Error('Should have failed'));
}, function () {
expect(called).to.be(true);
next();
})
.done();
});
});
describe('._savePkgMeta', function () {
before(function () {
mkdirp.sync(tempDir);
@@ -1061,6 +967,34 @@ describe('SvnResolver', function () {
});
});
describe('#parseSubversionListOutput', function () {
var list = [
' 12345 username Jan 1 12:34 ./',
' 12346 username Feb 2 12:34 branch-name/',
' 12347 username Mar 3 12:34 branch_name/',
' 12348 username Apr 4 12:34 branch.1.2.3/',
' 12349 username Jun 5 12:34 BranchName/'
].join('\r\n');
it('should not include the . (dot)path', function () {
var actual = SvnResolver.parseSubversionListOutput(list);
expect(actual).to.not.have.keys('.');
});
it('should parse path names with alphanumerics, dashes, dots and underscores', function () {
var actual = SvnResolver.parseSubversionListOutput(list);
expect(actual).to.eql({
'branch-name' : '12346',
'branch_name' : '12347',
'branch.1.2.3' : '12348',
'BranchName' : '12349'
});
});
});
// remote resolver tests
describe('.constructor', function () {
it('should guess the name from the path', function () {
@@ -1073,9 +1007,10 @@ describe('SvnResolver', function () {
expect(resolver.getName()).to.equal('svn');
});
});
describe('.resolve', function () {
it('should checkout correctly if resolution is a tag', function (next) {
it('should export correctly if resolution is a tag', function (next) {
var resolver = create({ source: 'file://' + testPackageAdmin, target: '0.0.1' });
resolver.resolve()
@@ -1091,7 +1026,7 @@ describe('SvnResolver', function () {
.done();
});
it('should checkout correctly if resolution is a commit', function (next) {
it('should export correctly if resolution is a commit', function (next) {
var resolver = create({ source: 'file://' + testPackageAdmin, target: 'r1' });
resolver.resolve()
@@ -1107,6 +1042,5 @@ describe('SvnResolver', function () {
})
.done();
});
});
});

View File

@@ -1,7 +1,6 @@
var expect = require('expect.js');
var path = require('path');
var fs = require('graceful-fs');
var path = require('path');
var nock = require('nock');
var Q = require('q');
var rimraf = require('rimraf');
@@ -13,7 +12,7 @@ var defaultConfig = require('../../../lib/config');
describe('UrlResolver', function () {
var testPackage = path.resolve(__dirname, '../../assets/package-a');
var tempDir = path.resolve(__dirname, '../../assets/tmp');
var tempDir = path.resolve(__dirname, '../../tmp/tmp');
var logger;
before(function (next) {
@@ -31,12 +30,12 @@ describe('UrlResolver', function () {
nock.cleanAll();
});
function create(decEndpoint, config) {
function create(decEndpoint) {
if (typeof decEndpoint === 'string') {
decEndpoint = { source: decEndpoint };
}
return new UrlResolver(decEndpoint, config || defaultConfig, logger);
return new UrlResolver(decEndpoint, defaultConfig(), logger);
}
describe('.constructor', function () {

View File

@@ -8,9 +8,9 @@ var scripts = require('../../lib/core/scripts.js');
describe('scripts', function () {
var tempDir = path.join(__dirname, '../assets/temp-scripts');
var tempDir = path.join(__dirname, '../tmp/temp-scripts');
var packageName = 'package-zip';
var packageDir = path.join('..', packageName + '.zip');
var packageDir = path.join(__dirname, '../assets/' + packageName + '.zip');
var config = {
cwd: tempDir,
@@ -125,4 +125,4 @@ describe('scripts', function () {
});
});
});

145
test/helpers.js Normal file
View File

@@ -0,0 +1,145 @@
var Q = require('q');
var path = require('path');
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 cmd = require('../lib/util/cmd');
var config = require('../lib/config');
var env = {
'GIT_AUTHOR_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
'GIT_AUTHOR_NAME': 'André Cruz',
'GIT_AUTHOR_EMAIL': 'amdfcruz@gmail.com',
'GIT_COMMITTER_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
'GIT_COMMITTER_NAME': 'André Cruz',
'GIT_COMMITTER_EMAIL': 'amdfcruz@gmail.com'
};
// Preserve the original environment
object.mixIn(env, process.env);
var tmpLocation = path.join(
os.tmpdir ? os.tmpdir() : os.tmpDir(),
'bower-tests',
uuid.v4().slice(0, 8)
);
exports.require = function (name) {
return require(path.join(__dirname, '../', name));
};
// We need to reset cache because tests are reusing temp directories
beforeEach(function () {
config.reset();
});
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) {
var that = this;
files = object.merge(files || {}, this.defaults);
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.read = function (name) {
return fs.readFileSync(path.join(this.path, name), 'utf8');
};
TempDir.prototype.git = function () {
var args = Array.prototype.slice.call(arguments);
return cmd('git', args, { cwd: this.path, env: env });
};
TempDir.prototype.exists = function (name) {
return fs.existsSync(path.join(this.path, name));
};
return TempDir;
})();
exports.expectEvent = function (emitter, eventName) {
var deferred = Q.defer();
emitter.once(eventName, function () {
deferred.resolve(arguments);
});
return deferred.promise;
};

View File

@@ -64,7 +64,7 @@ function checkRelease(dir, release) {
if (semver.valid(release)) {
return cmd('svn', ['list', 'tags'], { cwd: dir })
.spread(function (stdout) {
return stdout.split(/\s*\r*\n\s*/).some(function (tag) {
return stdout.split(/\/\s*\r*\n\s*/).some(function (tag) {
return semver.clean(tag) === release;
});
});
@@ -72,7 +72,7 @@ function checkRelease(dir, release) {
return cmd('svn', ['list', 'branches'], { cwd: dir })
.spread(function (stdout) {
return stdout.split(/\s*\r*\n\s*/).some(function (branch) {
return stdout.split(/\/\s*\r*\n\s*/).some(function (branch) {
branch = branch.trim().replace(/^\*?\s*/, '');
return branch === release;
});

View File

@@ -1,9 +1,5 @@
{
"package-svn": {
"0.0.1": {
"README.md": "",
"foo": "foo"
},
"0.0.1": {
"README.md": "",
"foo": "foo"

View File

@@ -62,12 +62,16 @@
"bar": "bar",
"baz": "baz",
"more/more-foo": "more-foo",
"main.js": "should not be ignored",
"more/docs/doc.html": "doc",
"more/assets/styles.css": "css",
"bower.json": {
"name": "a",
"version": "0.2.2",
"main": "main.js",
"ignore": [
"bower.json",
"main.js",
"test/",
"more/docs",
"more/assets/",

View File

@@ -17,3 +17,6 @@ require('./core/resolverFactory');
require('./core/resolveCache');
require('./core/packageRepository');
require('./core/scripts');
require('./core/Manager');
require('./commands/index.js');
require('./util/index.js');

105
test/util/analytics.js Normal file
View File

@@ -0,0 +1,105 @@
var expect = require('expect.js');
var proxyquire = require('proxyquire');
var object = require('mout').object;
describe('analytics', function () {
var mockAnalytics = function(stubs, promptResponse) {
return proxyquire('../../lib/util/analytics', {
insight: function () {
return object.merge(stubs || {}, {
askPermission: function (message, callback) {
callback(undefined, promptResponse);
}
});
},
});
};
describe('#setup', function () {
it('leaves analytics enabled if provided', function () {
var config = { analytics: true };
return mockAnalytics().setup(config).then(function () {
expect(config.analytics).to.be(true);
});
});
it('leaves analytics disabled if provided', function () {
var config = { analytics: false };
return mockAnalytics().setup(config).then(function () {
expect(config.analytics).to.be(false);
});
});
it('defaults to false if insight.optOut is true', function () {
var config = { };
return mockAnalytics({ optOut: true }).setup(config).then(function () {
expect(config.analytics).to.be(false);
});
});
it('defaults to true if insight.optOut is false', function () {
var config = { };
return mockAnalytics({ optOut: false }).setup(config).then(function () {
expect(config.analytics).to.be(true);
});
});
it('defaults to true if insight.optOut is undefined and noninteractive', function () {
var config = { };
return mockAnalytics({ optOut: undefined }).setup(config).then(function () {
expect(config.analytics).to.be(true);
});
});
it('defautls to true if interactive insights return true from prompt', function () {
var config = { interactive: true };
return mockAnalytics({ optOut: undefined }, true).setup(config).then(function () {
expect(config.analytics).to.be(true);
});
});
it('defautls to false if interactive insights return false from prompt', function () {
var config = { interactive: true };
return mockAnalytics({ optOut: undefined }, false).setup(config).then(function () {
expect(config.analytics).to.be(false);
});
});
});
describe('Tracker', function (next) {
it('tracks if analytics = true', function(next) {
var analytics = mockAnalytics({
track: function (arg) {
expect(arg).to.be('foo');
next();
}
});
new analytics.Tracker({
analytics: true
}).track('foo');
});
it('does not track if analytics = false', function () {
var analytics = mockAnalytics({
track: function (arg) {
throw new Error();
}
});
expect(function () {
new analytics.Tracker({
analytics: false
}).track('foo');
}).to.not.throwError();
});
});
});

4
test/util/index.js Normal file
View File

@@ -0,0 +1,4 @@
describe('util', function () {
require('./removeIgnores');
require('./analytics');
});

View File

@@ -0,0 +1,75 @@
var expect = require('expect.js');
var helpers = require('../helpers');
var glob = require('glob');
var Q = require('q');
var removeIgnores = require('../../lib/util/removeIgnores');
describe('removeIgnores', function () {
var tempDir = new helpers.TempDir({
'bower.json': {},
'index.js': 'Not to ignore',
'node_modules/underscore/index.js': 'Should be ignored'
});
var ignoreTest = function(dir, meta, leftovers) {
tempDir.prepare();
var deferred = Q.defer();
removeIgnores(dir, meta).then(function() {
glob('**/*.*', { cwd: dir }, function(cb, files) {
expect(files).to.eql(leftovers);
deferred.resolve();
});
});
return deferred.promise;
};
it('removes all files in directory', function () {
return ignoreTest(tempDir.path,
{ ignore: [ 'node_modules/**/*' ] },
[ 'bower.json', 'index.js' ]
);
});
it('removes whole directory', function () {
return ignoreTest(tempDir.path,
{ ignore: [ 'node_modules/' ] },
[ 'bower.json', 'index.js' ]
);
});
it('removes whole directory (no ending slash)', function () {
return ignoreTest(tempDir.path,
{ ignore: [ 'node_modules' ] },
[ 'bower.json', 'index.js' ]
);
});
it('removes all but one file', function() {
return ignoreTest(tempDir.path,
{ ignore: [ '**/*', '!bower.json' ] },
[ 'bower.json' ]
);
});
it('refuses to ignore bower.json', function() {
return ignoreTest(tempDir.path,
{ ignore: [ '**/*', '!index.js' ] },
[ 'bower.json', 'index.js' ]
);
});
it('removes all but one file deep down the tree', function() {
return ignoreTest(tempDir.path,
{ ignore: [ '**/*', '!node_modules/underscore/index.js' ] },
[
'bower.json',
'node_modules/underscore/index.js'
]
);
});
});