mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e97bf479fb | ||
|
|
fe2f71c9b8 | ||
|
|
ee6c483dd4 | ||
|
|
e83ab86f1f | ||
|
|
ef237fc521 | ||
|
|
fd4d68038b | ||
|
|
8e458c21e9 | ||
|
|
4ab36cb3bc | ||
|
|
2d149f3a09 | ||
|
|
dfd2c7a3d2 | ||
|
|
6637762aec | ||
|
|
615eba3b40 | ||
|
|
dc183125fc | ||
|
|
c3b69d1201 | ||
|
|
3e1a50ab9a | ||
|
|
7565c71eb2 | ||
|
|
d743352bc0 | ||
|
|
75ca72e3a8 | ||
|
|
59d26c6825 | ||
|
|
5eed363e10 | ||
|
|
49b2fdbde9 | ||
|
|
623f6e9542 | ||
|
|
32356f23d3 | ||
|
|
321ddabfd5 | ||
|
|
bf93a6a1ab | ||
|
|
91aa5dc6dd | ||
|
|
56ed46b99a | ||
|
|
daaf21a4fe | ||
|
|
06a8f2afab | ||
|
|
cc04530c4a | ||
|
|
0b6f62977a | ||
|
|
2a2996c22b | ||
|
|
254aba0995 | ||
|
|
23a81b3121 | ||
|
|
c4659f816f | ||
|
|
d1427e7d2e | ||
|
|
5584d1062e | ||
|
|
8cb41fe5fb | ||
|
|
cd893fec15 | ||
|
|
2a01f178da | ||
|
|
a069d1e07d | ||
|
|
76fa7f5200 | ||
|
|
7a0a86d51c | ||
|
|
8e283e43db | ||
|
|
794744d5a3 | ||
|
|
5c3e69b045 | ||
|
|
9cbd595cfd | ||
|
|
a5074eca7d | ||
|
|
9386b117c1 | ||
|
|
7738248230 | ||
|
|
85f1f808d3 | ||
|
|
6dbafa22bb | ||
|
|
cadcd37681 | ||
|
|
a45ea6e1d8 | ||
|
|
187f24de81 | ||
|
|
8df1f48226 | ||
|
|
7dbf332a94 | ||
|
|
c09ddf6de1 | ||
|
|
9b5fa99d6a | ||
|
|
b9abf32007 | ||
|
|
09ecb80625 | ||
|
|
c4e9a0e340 | ||
|
|
b6cf4e1826 | ||
|
|
ecb1619399 | ||
|
|
83f4b7b699 | ||
|
|
9b81ddf4d5 | ||
|
|
4af17f0e40 | ||
|
|
2fbc036e69 | ||
|
|
d236a12b8f | ||
|
|
232be333ad | ||
|
|
192e5af797 | ||
|
|
deb39b8f34 | ||
|
|
197b41d97a | ||
|
|
7b11a57c6f | ||
|
|
eac7945fae | ||
|
|
1177d2263f | ||
|
|
b9478a1f65 | ||
|
|
025cf91679 | ||
|
|
442aa72ccc | ||
|
|
1c09f9c82d | ||
|
|
c6d2f633ea | ||
|
|
17bd60e3e9 | ||
|
|
1d24e82276 | ||
|
|
e42d3d5620 | ||
|
|
5c83972401 | ||
|
|
b8df18481f | ||
|
|
1690dd4728 | ||
|
|
a86087ed7c | ||
|
|
745060f0b5 | ||
|
|
0a0a490a38 | ||
|
|
f816a5b0da | ||
|
|
2f02e49716 | ||
|
|
a98a0b1ac2 | ||
|
|
034326c984 | ||
|
|
a965b05400 | ||
|
|
6c2de4a359 | ||
|
|
e9588279c8 | ||
|
|
efe3a78499 | ||
|
|
31969a4939 | ||
|
|
7a00c5ac71 | ||
|
|
23fbbb5191 | ||
|
|
a58b1ccfa5 | ||
|
|
37b0a5c04c | ||
|
|
3fb4f60192 | ||
|
|
635fa84731 | ||
|
|
410bf4f0cd | ||
|
|
eebe115b78 | ||
|
|
70eb9f0678 | ||
|
|
b9de179fe8 | ||
|
|
ac29e24c1b | ||
|
|
d83572803d | ||
|
|
87faa4f108 | ||
|
|
ea3a0d64c1 | ||
|
|
8288d2f38b | ||
|
|
ff817dad0d | ||
|
|
ba554d5f45 | ||
|
|
90922a0ce0 | ||
|
|
a91cb546f9 | ||
|
|
3cb21d128f | ||
|
|
1b8d5d0648 | ||
|
|
03f035cdf0 | ||
|
|
ac95654409 | ||
|
|
ba33bb6aa4 | ||
|
|
2898c0507e | ||
|
|
d59edd6cca | ||
|
|
74d568b1e7 | ||
|
|
0316fedd4c | ||
|
|
5247d93d7b | ||
|
|
bda5312917 | ||
|
|
e8c071304c | ||
|
|
4e155caae9 | ||
|
|
aedb7e44ba | ||
|
|
c58109c1b4 | ||
|
|
2167c7b6d4 | ||
|
|
22eef989f6 | ||
|
|
edf387363d | ||
|
|
14ffb7443f | ||
|
|
c21ba1e64c | ||
|
|
46b8069c6d | ||
|
|
2fb697f452 | ||
|
|
73f3293314 | ||
|
|
53fc25550b | ||
|
|
961d1775e5 | ||
|
|
bdaa359d11 | ||
|
|
a90cc018dd | ||
|
|
e06f784d35 | ||
|
|
5ce1b6a4c1 | ||
|
|
86d579a7ae | ||
|
|
d031ab5ceb | ||
|
|
dcf7230494 | ||
|
|
20aca45b39 | ||
|
|
98ea43eca9 | ||
|
|
cd541c7a20 | ||
|
|
5a5cf31aba | ||
|
|
3074711828 | ||
|
|
aa97c928e9 | ||
|
|
5b4d6f2fee | ||
|
|
7120ad5014 | ||
|
|
6286cfac28 | ||
|
|
1af09da2aa | ||
|
|
bea533acf8 | ||
|
|
5316c2479e | ||
|
|
9895f8c71e | ||
|
|
9749a398c0 | ||
|
|
2883a278ee | ||
|
|
6bbb9ec956 | ||
|
|
630556f7fe | ||
|
|
32a79c4fa1 | ||
|
|
eabfee6890 | ||
|
|
6b9b283149 | ||
|
|
a19ad6663c | ||
|
|
dfae97a8d7 | ||
|
|
93d03434eb | ||
|
|
cfc061c960 | ||
|
|
09bc1784e4 | ||
|
|
b049bdb33f | ||
|
|
cc63a633b5 | ||
|
|
8016bd4e1f | ||
|
|
d130c73e77 | ||
|
|
3ecc389e66 | ||
|
|
99b0c8974b | ||
|
|
194a09f21d | ||
|
|
b87b8ea931 |
@@ -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
5
.gitignore
vendored
@@ -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
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -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
|
||||
|
||||
79
CHANGELOG.md
79
CHANGELOG.md
@@ -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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
23
Gruntfile.js
23
Gruntfile.js
@@ -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
345
README.md
@@ -1,152 +1,73 @@
|
||||
# Bower
|
||||
|
||||
[](http://travis-ci.org/bower/bower)
|
||||
[](https://travis-ci.org/bower/bower) [](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:
|
||||
|
||||

|
||||
@@ -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
|
||||
|
||||
|
||||
30
bin/bower
30
bin/bower
@@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
26
lib/commands/cache/clean.js
vendored
26
lib/commands/cache/clean.js
vendored
@@ -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 () {
|
||||
|
||||
4
lib/commands/cache/index.js
vendored
4
lib/commands/cache/index.js
vendored
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
clean: require('./clean'),
|
||||
list: require('./list')
|
||||
};
|
||||
26
lib/commands/cache/list.js
vendored
26
lib/commands/cache/list.js
vendored
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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')
|
||||
};
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -121,8 +121,6 @@ JsonRenderer.prototype.prompt = function (prompts) {
|
||||
});
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.updateNotice = function () {};
|
||||
|
||||
// -------------------------
|
||||
|
||||
JsonRenderer.prototype._stringify = function (log) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
91
package.json
91
package.json
@@ -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"
|
||||
|
||||
@@ -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>]"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
6
test/commands/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
describe('integration tests', function () {
|
||||
require('./init');
|
||||
require('./install');
|
||||
require('./uninstall');
|
||||
require('./update');
|
||||
});
|
||||
50
test/commands/init.js
Normal file
50
test/commands/init.js
Normal 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
205
test/commands/install.js
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
52
test/commands/uninstall.js
Normal file
52
test/commands/uninstall.js
Normal 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
239
test/commands/update.js
Normal 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
270
test/core/Manager.js
Normal 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 }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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 = [];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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
145
test/helpers.js
Normal 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;
|
||||
};
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{
|
||||
"package-svn": {
|
||||
"0.0.1": {
|
||||
"README.md": "",
|
||||
"foo": "foo"
|
||||
},
|
||||
"0.0.1": {
|
||||
"README.md": "",
|
||||
"foo": "foo"
|
||||
|
||||
@@ -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/",
|
||||
|
||||
@@ -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
105
test/util/analytics.js
Normal 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
4
test/util/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
describe('util', function () {
|
||||
require('./removeIgnores');
|
||||
require('./analytics');
|
||||
});
|
||||
75
test/util/removeIgnores.js
Normal file
75
test/util/removeIgnores.js
Normal 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'
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user