Merge branch 'master' into link-install-deps

This commit is contained in:
André Cruz
2014-03-25 11:49:05 +00:00
24 changed files with 361 additions and 148 deletions

View File

@@ -1,6 +1,16 @@
# Changelog
##1.2.8 - 2013-12-02
- Fix absolute paths ending with / not going through the FsResolver, ([#898](https://github.com/bower/bower/issues/898))
- Allow query string parameters in package URLs
- Swapped 'unzip' module for 'decompress-zip', and some other small unzipping fixes([#873](https://github.com/bower/bower/issues/873), [#896](https://github.com/bower/bower/issues/896))
- Allow the root-check to be overidden when calling bower programmatically.
- Fixed some bugs relating to packages with a very large dependency tree
- Fix a bug caused by a recent change to semver
## 1.2.7 - 2013-09-29
- Do not swallow sync errors when using the programmatic API ([#849](https://github.com/bower/bower/issues/849))

144
README.md
View File

@@ -1,4 +1,8 @@
# BOWER [![Build Status](https://secure.travis-ci.org/bower/bower.png?branch=master)](http://travis-ci.org/bower/bower)
# Bower
[![Build Status](https://secure.travis-ci.org/bower/bower.png?branch=master)](http://travis-ci.org/bower/bower) [![Views in the last 24 hours](https://sourcegraph.com/api/repos/github.com/bower/bower/counters/views-24h.png)](https://sourcegraph.com/github.com/bower/bower)
<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
@@ -31,27 +35,24 @@ packages require it to be fetched and installed.
Much more information is available via `bower help` once it's installed. This
is just enough to get you started.
#### Warning
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
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.
### Installing packages and dependencies
Bower offers several ways to install packages:
#####Using the dependencies listed in the current directory's bower.json
```
# Using the dependencies listed in the current directory's bower.json
bower install
# Using a local or remote package
```
##### Using a local or remote package
```
bower install <package>
# Using a specific version of a package
```
##### Using a specific version of a package
```
bower install <package>#<version>
# Using a different name and a specific version of a package
```
##### Using a different name and a specific version of a package
```
bower install <name>=<package>#<version>
```
@@ -75,8 +76,20 @@ 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.
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
@@ -94,7 +107,7 @@ The easiest approach is to use Bower statically, just reference the package's
installed components manually using a `script` tag:
```html
<script src="/bower_components/jquery/index.js"></script>
<script src="/bower_components/jquery/jquery.js"></script>
```
For more complex projects, you'll probably want to concatenate your scripts or
@@ -102,29 +115,6 @@ 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.
### 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).
### Uninstalling packages
To uninstall a locally installed package:
@@ -134,6 +124,43 @@ bower uninstall <package-name>
```
#### Warning
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
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
To use Bower on Windows, you must install
[msysgit](http://code.google.com/p/msysgit/) correctly. Be sure to check the
option shown below:
![msysgit](http://f.cl.ly/items/2V2O3i1p3R2F1r2v0a12/mysgit.png)
Note that if you use TortoiseGit and if Bower keeps asking for your SSH
password, you should add the following environment variable: `GIT_SSH -
C:\Program Files\TortoiseGit\bin\TortoisePlink.exe`. Adjust the `TortoisePlink`
path if needed.
### 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.
@@ -193,6 +220,29 @@ The `bower.json` defines several options:
}
```
### 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
@@ -277,20 +327,6 @@ bower completion >> ~/.bash_profile
```
## A note for Windows users
To use Bower on Windows, you must install
[msysgit](http://code.google.com/p/msysgit/) correctly. Be sure to check the
option shown below:
![msysgit](http://f.cl.ly/items/2V2O3i1p3R2F1r2v0a12/mysgit.png)
Note that if you use TortoiseGit and if Bower keeps asking for your SSH
password, you should add the following environment variable: `GIT_SSH -
C:\Program Files\TortoiseGit\bin\TortoisePlink.exe`. Adjust the `TortoisePlink`
path if needed.
## Contact
Have a question?

124
bin/bower
View File

@@ -11,6 +11,7 @@ var bower = require('../lib');
var pkg = require(path.join(__dirname, '..', 'package.json'));
var cli = require('../lib/util/cli');
var rootCheck = require('../lib/util/rootCheck');
var analytics = require('../lib/util/analytics');
// --------
@@ -69,71 +70,74 @@ while (options.argv.remain.length) {
options.argv.remain.pop();
}
// Execute the command
commandFunc = command && mout.object.get(bower.commands, command);
command = command && command.replace(/\./g, ' ');
// Ask for Insights on first run.
analytics.setup().then(function () {
// Execute the command
commandFunc = command && mout.object.get(bower.commands, command);
command = command && command.replace(/\./g, ' ');
// If no command was specified, show bower help
// Do the same if the command is unknown
if (!commandFunc) {
logger = bower.commands.help();
command = 'help';
// If the user requested help, show the command's help
// Do the same if the actual command is a group of other commands (e.g.: cache)
} else if (options.help || !commandFunc.line) {
logger = bower.commands.help(command);
command = 'help';
// Call the line method
} else {
logger = commandFunc.line(process.argv);
// If the method failed to interpret the process arguments
// show the command help
if (!logger) {
// If no command was specified, show bower help
// Do the same if the command is unknown
if (!commandFunc) {
logger = bower.commands.help();
command = 'help';
// If the user requested help, show the command's help
// Do the same if the actual command is a group of other commands (e.g.: cache)
} else if (options.help || !commandFunc.line) {
logger = bower.commands.help(command);
command = 'help';
}
}
// Call the line method
} else {
logger = commandFunc.line(process.argv);
// Get the renderer and configure it with the executed command
renderer = cli.getRenderer(command, logger.json, bower.config);
logger
.on('end', function (data) {
if (!bower.config.silent) {
renderer.end(data);
}
})
.on('error', function (err) {
if (levels.error >= loglevel) {
renderer.error(err);
// If the method failed to interpret the process arguments
// show the command help
if (!logger) {
logger = bower.commands.help(command);
command = 'help';
}
}
process.exit(1);
})
.on('log', function (log) {
if (levels[log.level] >= loglevel) {
renderer.log(log);
}
})
.on('prompt', function (prompt, callback) {
renderer.prompt(prompt)
.then(function (answer) {
callback(answer);
// Get the renderer and configure it with the executed command
renderer = cli.getRenderer(command, logger.json, bower.config);
logger
.on('end', function (data) {
if (!bower.config.silent) {
renderer.end(data);
}
})
.on('error', function (err) {
if (levels.error >= loglevel) {
renderer.error(err);
}
process.exit(1);
})
.on('log', function (log) {
if (levels[log.level] >= loglevel) {
renderer.log(log);
}
})
.on('prompt', function (prompt, callback) {
renderer.prompt(prompt)
.then(function (answer) {
callback(answer);
});
});
// Warn if HOME is not SET
if (!osenv.home()) {
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 (notifier.update && levels.info >= loglevel) {
renderer.updateNotice(notifier.update);
}
});
// Warn if HOME is not SET
if (!osenv.home()) {
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 (notifier.update && levels.info >= loglevel) {
renderer.updateNotice(notifier.update);
}

View File

@@ -31,7 +31,7 @@ function home(name, config) {
} else {
decEndpoint = endpointParser.decompose(name);
promise = project.getPackageRepository().fetch(decEndpoint)
.spread(function (canonicalDir, pkgMeta) {
.spread(function (canonicalDir, pkgMeta) {
return pkgMeta;
});
}

View File

@@ -4,17 +4,21 @@ 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) {
var repository;
var decEndpoint;
var tracker;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
repository = new PackageRepository(config, logger);
tracker = new Tracker(config);
decEndpoint = endpointParser.decompose(endpoint);
tracker.trackDecomposedEndpoints('info', [decEndpoint]);
Q.all([
getPkgMeta(repository, decEndpoint, property),

View File

@@ -3,25 +3,30 @@ 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) {
var project;
var decEndpoints;
var tracker;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
project = new Project(config, logger);
tracker = new Tracker(config);
// Convert endpoints to decomposed endpoints
endpoints = endpoints || [];
decEndpoints = endpoints.map(function (endpoint) {
return endpointParser.decompose(endpoint);
});
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);

View File

@@ -4,6 +4,7 @@ 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');
var createError = require('../util/createError');
var defaultConfig = require('../config');
@@ -12,11 +13,13 @@ var GitHubResolver = require('../core/resolvers/GitHubResolver');
function register(name, url, config) {
var repository;
var registryClient;
var tracker;
var logger = new Logger();
var force;
config = mout.object.deepFillIn(config || {}, defaultConfig);
force = config.force;
tracker = new Tracker(config);
// Bypass any cache
config.offline = false;
@@ -42,6 +45,8 @@ function register(name, url, config) {
}
}
tracker.track('register');
// Attempt to resolve the package referenced by the URL to ensure
// everything is ok before registering
repository = new PackageRepository(config, logger);
@@ -81,6 +86,7 @@ function register(name, url, config) {
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);

View File

@@ -3,17 +3,20 @@ 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) {
var registryClient;
var promise;
var tracker;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
config.cache = config.storage.registry;
registryClient = new RegistryClient(config, logger);
tracker = new Tracker(config);
// If no name was specified, list all packages
if (!name) {
@@ -25,6 +28,7 @@ function search(name, config) {
promise
.done(function (results) {
tracker.track('searched', name);
logger.emit('end', results);
}, function (error) {
logger.emit('error', error);

View File

@@ -3,15 +3,20 @@ 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) {
var project;
var tracker;
var logger = new Logger();
options = options || {};
config = mout.object.deepFillIn(config || {}, defaultConfig);
project = new Project(config, logger);
tracker = new Tracker(config);
tracker.trackNames('uninstall', names);
project.getTree()
.spread(function (tree, flattened) {
@@ -35,6 +40,7 @@ function uninstall(names, options, config) {
})
.done(function (uninstalled) {
logger.emit('end', uninstalled);
tracker.trackNames('uninstalled', Object.keys(uninstalled));
}, function (error) {
logger.emit('error', error);
});

View File

@@ -12,6 +12,12 @@ if (config.interactive == null) {
config.interactive = process.bin === 'bower' && tty.isatty(1);
}
// If `analytics` hasn't been explicitly set, we disable
// it when ran programatically.
if (config.analytics == null) {
config.analytics = config.interactive;
}
// Merge common CLI options into the config
mout.object.mixIn(config, cli.readOptions({
force: { type: Boolean, shorthand: 'f' },

View File

@@ -371,6 +371,8 @@ Manager.prototype._failFast = function () {
};
Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta, jsonKey) {
var pending = [];
decEndpoint.dependencies = decEndpoint.dependencies || {};
// Parse package dependencies
@@ -426,10 +428,7 @@ Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta, jsonKey)
}, this);
if (compatible) {
compatible.promise
.then(function () {
this._parseDependencies(decEndpoint, pkgMeta, jsonKey);
}.bind(this));
pending.push(compatible.promise);
return;
}
}
@@ -442,6 +441,13 @@ Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta, jsonKey)
childDecEndpoint.dependants = [decEndpoint];
this._fetch(childDecEndpoint);
}, this);
if (pending.length > 0) {
Q.all(pending)
.then(function () {
this._parseDependencies(decEndpoint, pkgMeta, jsonKey);
}.bind(this));
}
};
Manager.prototype._dissect = function () {
@@ -791,7 +797,7 @@ Manager.prototype._areCompatible = function (candidate, resolved) {
// 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)) {
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');

View File

@@ -60,7 +60,7 @@ function getConstructor(source, config, registryClient) {
// If source is ./ or ../ or an absolute path
absolutePath = path.resolve(config.cwd, source);
if (/^\.\.?[\/\\]/.test(source) || /^~\//.test(source) || path.normalize(source) === absolutePath) {
if (/^\.\.?[\/\\]/.test(source) || /^~\//.test(source) || path.normalize(source).replace(/[\/\\]+$/, '') === absolutePath) {
promise = Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
.then(function (stats) {
if (stats.isDirectory()) {

View File

@@ -19,6 +19,12 @@ function FsResolver(decEndpoint, config, logger) {
if (this._target !== '*') {
throw createError('File system sources can\'t resolve targets', 'ENORESTARGET');
}
// If the name was guessed
if (this._guessedName) {
// Remove extension
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
}
}
util.inherits(FsResolver, Resolver);

View File

@@ -12,8 +12,6 @@ var extract = require('../../util/extract');
var createError = require('../../util/createError');
function UrlResolver(decEndpoint, config, logger) {
var pos;
Resolver.call(this, decEndpoint, config, logger);
// If target was specified, error out
@@ -21,12 +19,12 @@ function UrlResolver(decEndpoint, config, logger) {
throw createError('URL sources can\'t resolve targets', 'ENORESTARGET');
}
// If the name was guessed, remove the ? part
// If the name was guessed
if (this._guessedName) {
pos = this._name.indexOf('?');
if (pos !== -1) {
this._name = path.basename(this._name.substr(0, pos));
}
// Remove the ?xxx part
this._name = this._name.replace(/\?.*$/, '');
// Remove extension
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
}
this._remote = url.parse(this._source);
@@ -107,7 +105,8 @@ UrlResolver.prototype._resolve = function () {
// -----------------
UrlResolver.prototype._download = function () {
var file = path.join(this._tempDir, path.basename(this._source));
var fileName = url.parse(path.basename(this._source)).pathname;
var file = path.join(this._tempDir, fileName);
var reqHeaders = {};
var that = this;
@@ -199,6 +198,8 @@ UrlResolver.prototype._extract = function (file, response) {
if (mimeType) {
// Clean everything after ; and trim the end result
mimeType = mimeType.split(';')[0].trim();
// Some servers add quotes around the content-type, so we trim that also
mimeType = mout.string.trim(mimeType, ['"', '\'']);
}
if (!extract.canExtract(file, mimeType)) {

View File

@@ -419,7 +419,7 @@ StandardRenderer.prototype._tree2archy = function (node) {
// State labels
if (node.missing) {
label += chalk.red(' missing');
label += chalk.red(' not installed');
return label;
}

61
lib/util/analytics.js Normal file
View File

@@ -0,0 +1,61 @@
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;
// Initializes the application-wide insight singleton and asks for the
// permission on the CLI during the first run.
analytics.setup = function setup() {
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);
} else {
deferred.resolve();
}
return deferred.promise;
};
var Tracker = analytics.Tracker = function Tracker(config) {
if (!config.analytics) {
this.track = function noop() {};
}
};
Tracker.prototype.track = function track() {
if (!insight) {
throw new Error('You must call analytics.setup() prior to tracking.');
}
insight.track.apply(insight, arguments);
};
Tracker.prototype.trackDecomposedEndpoints = function trackDecomposedEndpoints(command, endpoints) {
endpoints.forEach(function (endpoint) {
this.track(command, endpoint.source, endpoint.target);
}.bind(this));
};
Tracker.prototype.trackPackages = function trackPackages(command, packages) {
mout.object.forOwn(packages, function (package) {
var meta = package.pkgMeta;
this.track(command, meta.name, meta.version);
}.bind(this));
};
Tracker.prototype.trackNames = function trackNames(command, names) {
names.forEach(function (name) {
this.track(command, name);
}.bind(this));
};

View File

@@ -3,8 +3,15 @@ var path = require('path');
var Q = require('q');
var mout = require('mout');
var which = require('which');
var PThrottler = require('p-throttler');
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);
var winBatchExtensions;
var winWhichCache;
var isWin = process.platform === 'win32';
@@ -44,7 +51,7 @@ function getWindowsCommand(command) {
// If an error occurs, a meaningful error is generated
// Returns a promise that gets fulfilled if the command succeeds
// or rejected if it fails
function cmd(command, args, options) {
function executeCmd(command, args, options) {
var process;
var stderr = '';
var stdout = '';
@@ -103,4 +110,8 @@ function cmd(command, args, options) {
return deferred.promise;
}
function cmd(command, args, options) {
return throttler.enqueue(executeCmd.bind(null, command, args, options));
}
module.exports = cmd;

View File

@@ -1,7 +1,7 @@
var path = require('path');
var fs = require('graceful-fs');
var zlib = require('zlib');
var unzip = require('unzip');
var DecompressZip = require('decompress-zip');
var tar = require('tar');
var Q = require('q');
var mout = require('mout');
@@ -22,6 +22,7 @@ extractors = {
'.tgz': extractTarGz,
'.gz': extractGz,
'application/zip': extractZip,
'application/x-zip': extractZip,
'application/x-tar': extractTar,
'application/x-tgz': extractTarGz,
'application/x-gzip': extractGz
@@ -32,15 +33,14 @@ extractorTypes = Object.keys(extractors);
function extractZip(archive, dst) {
var deferred = Q.defer();
fs.createReadStream(archive)
new DecompressZip(archive)
.on('error', deferred.reject)
.pipe(unzip.Extract({
.on('extract', deferred.resolve.bind(deferred, dst))
.extract({
path: dst,
follow: false, // Do not follow symlinks (#699)
filter: filterSymlinks // Filter symlink files
}))
.on('error', deferred.reject)
.on('close', deferred.resolve.bind(deferred, dst));
});
return deferred.promise;
}

View File

@@ -10,7 +10,7 @@ function rootCheck(options, config) {
var errorMsg;
// Allow running the command as root
if (options.allowRoot) {
if (options.allowRoot || config.allowRoot) {
return;
}

View File

@@ -1,6 +1,6 @@
{
"name": "bower",
"version": "1.2.7",
"version": "1.2.8",
"description": "The browser package manager.",
"author": "Twitter",
"licenses": [
@@ -29,6 +29,7 @@
"cardinal": "~0.4.0",
"chalk": "~0.2.0",
"chmodr": "~0.1.0",
"decompress-zip": "~0.0.3",
"fstream": "~0.1.22",
"fstream-ignore": "~0.0.6",
"glob": "~3.2.1",
@@ -53,9 +54,10 @@
"sudo-block": "~0.2.0",
"tar": "~0.1.17",
"tmp": "~0.0.20",
"unzip": "~0.1.7",
"update-notifier": "~0.1.3",
"which": "~1.0.5"
"which": "~1.0.5",
"p-throttler": "~0.0.1",
"insight": "~0.2.0"
},
"devDependencies": {
"expect.js": "~0.2.0",

View File

@@ -1,6 +1,6 @@
{
"command": "list",
"description": "List local packages.",
"description": "List local packages - and possible updates.",
"usage": [
"list [<options>]"
],

View File

@@ -303,6 +303,11 @@ describe('resolverFactory', function () {
temp = path.resolve(__dirname, '../assets/package-a');
endpoints[temp] = temp;
// Absolute path that ends with a /
// See: https://github.com/bower/bower/issues/898
temp = path.resolve(__dirname, '../assets/package-a') + '/';
endpoints[temp] = temp;
// Relative path
endpoints[__dirname + '/../assets/package-a'] = temp;

View File

@@ -45,9 +45,9 @@ describe('FsResolver', function () {
describe('.constructor', function () {
it('should guess the name from the path', function () {
var resolver = create(testPackage);
var resolver = create(path.resolve('../../assets/package-zip.zip'));
expect(resolver.getName()).to.equal('package-a');
expect(resolver.getName()).to.equal('package-zip');
});
it('should make paths absolute and normalized', function () {

View File

@@ -43,13 +43,13 @@ describe('UrlResolver', function () {
it('should guess the name from the URL', function () {
var resolver = create('http://bower.io/foo.txt');
expect(resolver.getName()).to.equal('foo.txt');
expect(resolver.getName()).to.equal('foo');
});
it('should remove ?part from the URL when guessing the name', function () {
var resolver = create('http://bower.io/foo.txt?bar');
expect(resolver.getName()).to.equal('foo.txt');
expect(resolver.getName()).to.equal('foo');
});
it('should not guess the name or remove ?part from the URL if not guessing', function () {
@@ -430,6 +430,11 @@ describe('UrlResolver', function () {
'Content-Type': ' application/zip ; charset=UTF-8'
})
.get('/package-zip4')
.replyWithFile(200, path.resolve(__dirname, '../../assets/package-zip.zip'), {
'Content-Type': '"application/x-zip"' // Test with quotes
})
.get('/package-tar')
.replyWithFile(200, path.resolve(__dirname, '../../assets/package-tar.tar.gz'), {
'Content-Type': ' application/x-tgz ; charset=UTF-8'
@@ -462,7 +467,7 @@ describe('UrlResolver', function () {
expect(fs.existsSync(path.join(dir, 'foo.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'bar.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'package-zip'))).to.be(false);
expect(fs.existsSync(path.join(dir, 'package-zip2.zip'))).to.be(false);
expect(fs.existsSync(path.join(dir, 'package-zip3.zip'))).to.be(false);
resolver = create('http://bower.io/package-zip3');
@@ -472,7 +477,16 @@ describe('UrlResolver', function () {
expect(fs.existsSync(path.join(dir, 'foo.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'bar.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'package-zip'))).to.be(false);
expect(fs.existsSync(path.join(dir, 'package-zip3.zip'))).to.be(false);
expect(fs.existsSync(path.join(dir, 'package-zip4.zip'))).to.be(false);
resolver = create('http://bower.io/package-zip4');
return resolver.resolve();
})
.then(function (dir) {
expect(fs.existsSync(path.join(dir, 'foo.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'bar.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'package-tar'))).to.be(false);
resolver = create('http://bower.io/package-tar');
@@ -554,6 +568,32 @@ describe('UrlResolver', function () {
.done();
});
it('should allow for query strings in URL', function (next) {
var resolver;
nock('http://bower.io')
.get('/foo.js?bar=baz')
.reply(200, 'foo contents');
resolver = create('http://bower.io/foo.js?bar=baz');
resolver.resolve()
.then(function (dir) {
var contents;
expect(fs.existsSync(path.join(dir, 'index.js'))).to.be(true);
expect(fs.existsSync(path.join(dir, 'foo.js'))).to.be(false);
expect(fs.existsSync(path.join(dir, 'foo.js?bar=baz'))).to.be(false);
contents = fs.readFileSync(path.join(dir, 'index.js')).toString();
expect(contents).to.equal('foo contents');
assertMain(dir, 'index.js')
.then(next.bind(next, null));
})
.done();
});
it('should save cache headers', function (next) {
var resolver;