update gitignore...

This commit is contained in:
Samuele E. Locatelli
2019-12-18 18:03:00 +01:00
parent 7deb7ea8d3
commit 6029e267a3
3062 changed files with 1 additions and 608998 deletions
+1
View File
@@ -65,3 +65,4 @@ SiteSelector/bin/
StockManMVC/bin/
VersGen/bin/Debug/
/packages
-3
View File
@@ -1,3 +0,0 @@
@echo off
SET PATH=%~dp0;%PATH%
"%~dp0node" "%~dp0..\..\packages\Bower.1.3.11\node_modules\bower\bin\bower" %*
-60
View File
@@ -1,60 +0,0 @@
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
jshint: {
options: {
jshintrc: '.jshintrc'
},
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']
},
short: {
options: {
reporter: 'dot'
},
src: ['test/test.js']
}
},
exec: {
assets: {
command: 'node test/packages.js && node test/packages-svn.js'
},
'assets-force': {
command: 'node test/packages.js --force && node test/packages-svn.js --force'
},
cover: {
command: '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: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'simplemocha:short']
}
});
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');
};
-145
View File
@@ -1,145 +0,0 @@
#!/usr/bin/env node
'use strict';
process.bin = process.title = 'bower';
var Q = require('q');
var mout = require('mout');
var Logger = require('bower-logger');
var osenv = require('osenv');
var bower = require('../lib');
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 levels = Logger.LEVELS;
options = cli.readOptions({
version: { type: Boolean, shorthand: 'v' },
help: { type: Boolean, shorthand: 'h' },
'allow-root': { type: Boolean }
});
// Handle print of version
if (options.version) {
process.stdout.write(pkg.version + '\n');
process.exit();
}
// Root check
rootCheck(options, bower.config);
// Set loglevel
if (bower.config.silent) {
loglevel = levels.error;
} else if (bower.config.verbose) {
loglevel = -Infinity;
Q.longStackSupport = true;
} else if (bower.config.quiet) {
loglevel = levels.warn;
} else {
loglevel = levels[bower.config.loglevel] || levels.info;
}
// Get the command to execute
while (options.argv.remain.length) {
command = options.argv.remain.join(' ');
// Alias lookup
if (bower.abbreviations[command]) {
command = bower.abbreviations[command].replace(/\s/g, '.');
break;
}
command = command.replace(/\s/g, '.');
// Direct lookup
if (mout.object.has(bower.commands, command)) {
break;
}
options.argv.remain.pop();
}
// Ask for Insights on first run.
analytics.setup(bower.config).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) {
logger = bower.commands.help(command);
command = 'help';
}
}
// Get the renderer and configure it with the executed command
renderer = cli.getRenderer(command, logger.json, bower.config);
logger
.on('end', function (data) {
if (!bower.config.silent && !bower.config.quiet) {
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');
}
if (bower.config.interactive) {
var updateNotifier = require('update-notifier');
// Check for newer version of Bower
var notifier = updateNotifier({
packageName: pkg.name,
packageVersion: pkg.version
});
if (notifier.update && levels.info >= loglevel) {
notifier.notify();
}
}
});
-205
View File
@@ -1,205 +0,0 @@
var fs = require('graceful-fs');
var path = require('path');
var mout = require('mout');
var Q = require('q');
var rimraf = require('rimraf');
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(logger, endpoints, options, config) {
var decEndpoints;
var names;
options = options || {};
config = defaultConfig(config);
// If endpoints is an empty array, null them
if (endpoints && !endpoints.length) {
endpoints = null;
}
// Generate decomposed endpoints and names based on the endpoints
if (endpoints) {
decEndpoints = endpoints.map(function (endpoint) {
return endpointParser.decompose(endpoint);
});
names = decEndpoints.map(function (decEndpoint) {
return decEndpoint.name || decEndpoint.source;
});
}
return Q.all([
clearPackages(decEndpoints, config, logger),
clearLinks(names, config, logger),
!names ? clearCompletion(config, logger) : null
])
.spread(function (entries) {
return entries;
});
}
function clearPackages(decEndpoints, config, logger) {
var repository = new PackageRepository(config, logger);
return repository.list()
.then(function (entries) {
var promises;
// Filter entries according to the specified packages
if (decEndpoints) {
entries = entries.filter(function (entry) {
return !!mout.array.find(decEndpoints, function (decEndpoint) {
var entryPkgMeta = entry.pkgMeta;
// Check if name or source match the entry
if (decEndpoint.name !== entryPkgMeta.name &&
decEndpoint.source !== entryPkgMeta.name &&
decEndpoint.source !== entryPkgMeta._source
) {
return false;
}
// If target is a wildcard, simply return true
if (decEndpoint.target === '*') {
return true;
}
// If it's a semver target, compare using semver spec
if (semver.validRange(decEndpoint.target)) {
return semver.satisfies(entryPkgMeta.version, decEndpoint.target);
}
// Otherwise, compare against target/release
return decEndpoint.target === entryPkgMeta._target ||
decEndpoint.target === entryPkgMeta._release;
});
});
}
promises = entries.map(function (entry) {
return repository.eliminate(entry.pkgMeta)
.then(function () {
logger.info('deleted', 'Cached package ' + entry.pkgMeta.name + ': ' + entry.canonicalDir, {
file: entry.canonicalDir
});
});
});
return Q.all(promises)
.then(function () {
if (!decEndpoints) {
// Ensure that everything is cleaned,
// even invalid packages in the cache
return repository.clear();
}
})
.then(function () {
return entries;
});
});
}
function clearLinks(names, config, logger) {
var promise;
var dir = config.storage.links;
// If no names are passed, grab all links
if (!names) {
promise = Q.nfcall(fs.readdir, dir)
.fail(function (err) {
if (err.code === 'ENOENT') {
return [];
}
throw err;
});
// Otherwise use passed ones
} else {
promise = Q.resolve(names);
}
return promise
.then(function (names) {
var promises;
var linksToRemove = [];
// Decide which links to delete
promises = names.map(function (name) {
var link = path.join(config.storage.links, name);
return Q.nfcall(fs.readlink, link)
.then(function (linkTarget) {
// Link exists, check if it points to a folder
// that still exists
return Q.nfcall(fs.stat, linkTarget)
.then(function (stat) {
// Target is not a folder..
if (!stat.isDirectory()) {
linksToRemove.push(link);
}
})
// Error occurred reading the link
.fail(function () {
linksToRemove.push(link);
});
// Ignore if link does not exist
}, function (err) {
if (err.code !== 'ENOENT') {
linksToRemove.push(link);
}
});
});
return Q.all(promises)
.then(function () {
var promises;
// Remove each link that was declared as invalid
promises = linksToRemove.map(function (link) {
return Q.nfcall(rimraf, link)
.then(function () {
logger.info('deleted', 'Invalid link: ' + link, {
file: link
});
});
});
return Q.all(promises);
});
});
}
function clearCompletion(config, logger) {
var dir = config.storage.completion;
return Q.nfcall(fs.stat, dir)
.then(function () {
return Q.nfcall(rimraf, dir)
.then(function () {
logger.info('deleted', 'Completion cache', {
file: dir
});
});
}, function (error) {
if (error.code !== 'ENOENT') {
throw error;
}
});
}
// -------------------
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 () {
// TODO:
};
module.exports = clean;
-44
View File
@@ -1,44 +0,0 @@
var mout = require('mout');
var PackageRepository = require('../../core/PackageRepository');
var cli = require('../../util/cli');
var defaultConfig = require('../../config');
function list(logger, packages, options, config) {
var repository;
config = defaultConfig(config);
repository = new PackageRepository(config, logger);
// If packages is an empty array, null them
if (packages && !packages.length) {
packages = null;
}
return repository.list()
.then(function (entries) {
if (packages) {
// Filter entries according to the specified packages
entries = entries.filter(function (entry) {
return !!mout.array.find(packages, function (pkg) {
return pkg === entry.pkgMeta.name;
});
});
}
return entries;
});
}
// -------------------
list.line = function (logger, argv) {
var options = cli.readOptions(argv);
var packages = options.argv.remain.slice(2);
return list(logger, packages, options);
};
list.completion = function () {
// TODO:
};
module.exports = list;
-21
View File
@@ -1,21 +0,0 @@
var Q = require('q');
var cli = require('../util/cli');
function completion(config) {
return new Q();
}
// -------------------
completion.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
return completion(logger, name);
};
completion.completion = function () {
// TODO:
};
module.exports = completion;
-42
View File
@@ -1,42 +0,0 @@
var Q = require('q');
var path = require('path');
var fs = require('graceful-fs');
var cli = require('../util/cli');
var createError = require('../util/createError');
function help(logger, name) {
var json;
if (name) {
json = path.resolve(__dirname, '../../templates/json/help-' + name.replace(/\s+/g, '/') + '.json');
} else {
json = path.resolve(__dirname, '../../templates/json/help.json');
}
return Q.promise(function (resolve) {
fs.exists(json, resolve);
})
.then(function (exists) {
if (!exists) {
throw createError('Unknown command: ' + name, 'EUNKOWNCMD', {
command: name
});
}
return require(json);
});
}
// -------------------
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 () {
// TODO
};
module.exports = help;
-62
View File
@@ -1,62 +0,0 @@
var Project = require('../core/Project');
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(logger, name, config) {
var project;
var promise;
var decEndpoint;
config = defaultConfig(config);
project = new Project(config, logger);
// Get the package meta
// If no name is specified, read the project json
// If a name is specified, fetch from the package repository
if (!name) {
promise = project.hasJson()
.then(function (json) {
if (!json) {
throw createError('You are not inside a package', 'ENOENT');
}
return project.getJson();
});
} else {
decEndpoint = endpointParser.decompose(name);
promise = project.getPackageRepository().fetch(decEndpoint)
.spread(function (canonicalDir, pkgMeta) {
return pkgMeta;
});
}
// Get homepage and open it
return promise.then(function (pkgMeta) {
var homepage = pkgMeta.homepage;
if (!homepage) {
throw createError('No homepage set for ' + pkgMeta.name, 'ENOHOME');
}
open(homepage);
return homepage;
});
}
// -------------------
home.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
return home(logger, name);
};
home.completion = function () {
// TODO:
};
module.exports = home;
-71
View File
@@ -1,71 +0,0 @@
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: {
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')
};
-72
View File
@@ -1,72 +0,0 @@
var mout = require('mout');
var Q = require('q');
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(logger, endpoint, property, config) {
var repository;
var decEndpoint;
var tracker;
config = defaultConfig(config);
repository = new PackageRepository(config, logger);
tracker = new Tracker(config);
decEndpoint = endpointParser.decompose(endpoint);
tracker.trackDecomposedEndpoints('info', [decEndpoint]);
return Q.all([
getPkgMeta(repository, decEndpoint, property),
decEndpoint.target === '*' && !property ? repository.versions(decEndpoint.source) : null
])
.spread(function (pkgMeta, versions) {
if (versions) {
return {
name: decEndpoint.source,
versions: versions,
latest: pkgMeta
};
}
return pkgMeta;
});
}
function getPkgMeta(repository, decEndpoint, property) {
return repository.fetch(decEndpoint)
.spread(function (canonicalDir, pkgMeta) {
pkgMeta = mout.object.filter(pkgMeta, function (value, key) {
return key.charAt(0) !== '_';
});
// Retrieve specific property
if (property) {
pkgMeta = mout.object.get(pkgMeta, property);
}
return pkgMeta;
});
}
// -------------------
info.line = function (logger, argv) {
var options = cli.readOptions(argv);
var pkg = options.argv.remain[1];
var property = options.argv.remain[2];
if (!pkg) {
return new Q();
}
return info(logger, pkg, property);
};
info.completion = function () {
// TODO:
};
module.exports = info;
-332
View File
@@ -1,332 +0,0 @@
var mout = require('mout');
var fs = require('graceful-fs');
var path = require('path');
var Q = require('q');
var endpointParser = require('bower-endpoint-parser');
var Project = require('../core/Project');
var defaultConfig = require('../config');
var GitHubResolver = require('../core/resolvers/GitHubResolver');
var GitFsResolver = require('../core/resolvers/GitFsResolver');
var cli = require('../util/cli');
var cmd = require('../util/cmd');
var createError = require('../util/createError');
function init(logger, config) {
var project;
config = defaultConfig(config);
// This command requires interactive to be enabled
if (!config.interactive) {
process.nextTick(function () {
logger.emit('error', createError('Register requires an interactive shell', 'ENOINT', {
details: 'Note that you can manually force an interactive shell with --config.interactive'
}));
});
return logger;
}
project = new Project(config, logger);
// Start with existing JSON details
return readJson(project, logger)
// Fill in defaults
.then(setDefaults.bind(null, config))
// Now prompt user to make changes
.then(promptUser.bind(null, logger))
// Set ignore based on the response
.spread(setIgnore.bind(null, config))
// Set dependencies based on the response
.spread(setDependencies.bind(null, project))
// All done!
.spread(saveJson.bind(null, project, logger));
}
function readJson(project, logger) {
return project.hasJson()
.then(function (json) {
if (json) {
logger.warn('existing', 'The existing ' + path.basename(json) + ' file will be used and filled in');
}
return project.getJson();
});
}
function saveJson(project, logger, json) {
// Cleanup empty props (null values, empty strings, objects and arrays)
mout.object.forOwn(json, function (value, key) {
if (value == null || mout.lang.isEmpty(value)) {
delete json[key];
}
});
logger.info('json', 'Generated json', { json: json });
// Confirm the json with the user
return Q.nfcall(logger.prompt.bind(logger), {
type: 'confirm',
message: 'Looks good?',
default: true
})
.then(function (good) {
if (!good) {
return null;
}
// Save json (true forces file creation)
return project.saveJson(true);
});
}
function setDefaults(config, json) {
var name;
var promise = Q.resolve();
// Name
if (!json.name) {
json.name = path.basename(config.cwd);
}
// Version
if (!json.version) {
// Assume latest semver tag if it's a git repo
promise = promise.then(function () {
return GitFsResolver.versions(config.cwd)
.then(function (versions) {
json.version = versions[0] || '0.0.0';
}, function () {
json.version = '0.0.0';
});
});
}
// Main
if (!json.main) {
// Remove '.js' from the end of the package name if it is there
name = path.basename(json.name, '.js');
if (fs.existsSync(path.join(config.cwd, 'index.js'))) {
json.main = 'index.js';
} else if (fs.existsSync(path.join(config.cwd, name + '.js'))) {
json.main = name + '.js';
}
}
// Homepage
if (!json.homepage) {
// Set as GitHub homepage if it's a GitHub repository
promise = promise.then(function () {
return cmd('git', ['config', '--get', 'remote.origin.url'])
.spread(function (stdout) {
var pair;
stdout = stdout.trim();
if (!stdout) {
return;
}
pair = GitHubResolver.getOrgRepoPair(stdout);
if (pair) {
json.homepage = 'https://github.com/' + pair.org + '/' + pair.repo;
}
})
.fail(function () { });
});
}
if (!json.authors) {
promise = promise.then(function () {
// Get the user name configured in git
return cmd('git', ['config', '--get', '--global', 'user.name'])
.spread(function (stdout) {
var gitEmail;
var gitName = stdout.trim();
// Abort if no name specified
if (!gitName) {
return;
}
// Get the user email configured in git
return cmd('git', ['config', '--get', '--global', 'user.email'])
.spread(function (stdout) {
gitEmail = stdout.trim();
}, function () {})
.then(function () {
json.authors = gitName;
json.authors += gitEmail ? ' <' + gitEmail + '>' : '';
});
}, function () {});
});
}
return promise.then(function () {
return json;
});
}
function promptUser(logger, json) {
var questions = [
{
'name': 'name',
'message': 'name',
'default': json.name,
'type': 'input'
},
{
'name': 'version',
'message': 'version',
'default': json.version,
'type': 'input'
},
{
'name': 'description',
'message': 'description',
'default': json.description,
'type': 'input'
},
{
'name': 'main',
'message': 'main file',
'default': json.main,
'type': 'input'
},
{
'name': 'moduleType',
'message': 'what types of modules does this package expose?',
'type': 'checkbox',
'choices': ['amd', 'es6', 'globals', 'node', 'yui']
},
{
'name': 'keywords',
'message': 'keywords',
'default': json.keywords ? json.keywords.toString() : null,
'type': 'input'
},
{
'name': 'authors',
'message': 'authors',
'default': json.authors ? json.authors.toString() : null,
'type': 'input'
},
{
'name': 'license',
'message': 'license',
'default': json.license || 'MIT',
'type': 'input'
},
{
'name': 'homepage',
'message': 'homepage',
'default': json.homepage,
'type': 'input'
},
{
'name': 'dependencies',
'message': 'set currently installed components as dependencies?',
'default': !mout.object.size(json.dependencies) && !mout.object.size(json.devDependencies),
'type': 'confirm'
},
{
'name': 'ignore',
'message': 'add commonly ignored files to ignore list?',
'default': true,
'type': 'confirm'
},
{
'name': 'private',
'message': 'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
'default': !!json.private,
'type': 'confirm'
}
];
return Q.nfcall(logger.prompt.bind(logger), questions)
.then(function (answers) {
json.name = answers.name;
json.version = answers.version;
json.description = answers.description;
json.main = answers.main;
json.moduleType = answers.moduleType;
json.keywords = toArray(answers.keywords);
json.authors = toArray(answers.authors, ',');
json.license = answers.license;
json.homepage = answers.homepage;
json.private = answers.private || null;
return [json, answers];
});
}
function toArray(value, splitter) {
var arr = value.split(splitter || /[\s,]/);
// Trim values
arr = arr.map(function (item) {
return item.trim();
});
// Filter empty values
arr = arr.filter(function (item) {
return !!item;
});
return arr.length ? arr : null;
}
function setIgnore(config, json, answers) {
if (answers.ignore) {
json.ignore = mout.array.combine(json.ignore || [], [
'**/.*',
'node_modules',
'bower_components',
config.directory,
'test',
'tests'
]);
}
return [json, answers];
}
function setDependencies(project, json, answers) {
if (answers.dependencies) {
return project.getTree()
.spread(function (tree, flattened, extraneous) {
if (extraneous.length) {
json.dependencies = {};
// Add extraneous as dependencies
extraneous.forEach(function (extra) {
var jsonEndpoint;
// Skip linked packages
if (extra.linked) {
return;
}
jsonEndpoint = endpointParser.decomposed2json(extra.endpoint);
mout.object.mixIn(json.dependencies, jsonEndpoint);
});
}
return [json, answers];
});
}
return [json, answers];
}
// -------------------
init.line = function (logger, argv) {
var options = cli.readOptions(argv);
return init(logger, options);
};
init.completion = function () {
// TODO:
};
module.exports = init;
-50
View File
@@ -1,50 +0,0 @@
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(logger, endpoints, options, config) {
var project;
var decEndpoints;
var tracker;
options = options || {};
config = defaultConfig(config);
if (options.save === undefined) {
options.save = config.defaultSave;
}
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);
return project.install(decEndpoints, options, config);
}
// -------------------
install.line = function (logger, argv) {
var options = install.options(argv);
return install(logger, options.argv.remain.slice(1), options);
};
install.options = function (argv) {
return cli.readOptions({
'force-latest': { type: Boolean, shorthand: 'F'},
'production': { type: Boolean, shorthand: 'p' },
'save': { type: Boolean, shorthand: 'S' },
'save-dev': { type: Boolean, shorthand: 'D' }
}, argv);
};
install.completion = function () {
// TODO:
};
module.exports = install;
-87
View File
@@ -1,87 +0,0 @@
var path = require('path');
var rimraf = require('rimraf');
var Q = require('q');
var Project = require('../core/Project');
var createLink = require('../util/createLink');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function link(logger, name, localName) {
if (name) {
return linkTo(logger, name, localName);
} else {
return linkSelf(logger);
}
}
function linkSelf(logger, config) {
var project;
config = defaultConfig(config);
project = new Project(config, logger);
return project.getJson()
.then(function (json) {
var src = config.cwd;
var dst = path.join(config.storage.links, json.name);
// Delete previous link if any
return Q.nfcall(rimraf, dst)
// Link globally
.then(function () {
return createLink(src, dst);
})
.then(function () {
return {
src: src,
dst: dst
};
});
});
}
function linkTo(logger, name, localName, config) {
var src;
var dst;
var project;
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
return Q.nfcall(rimraf, dst)
// Link locally
.then(function () {
return createLink(src, dst);
})
// Install linked package deps
.then(function () {
return project.update([localName]);
})
.then(function (installed) {
return {
src: src,
dst: dst,
installed: installed
};
});
}
// -------------------
link.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
var localName = options.argv.remain[2];
return link(logger, name, localName);
};
link.completion = function () {
// TODO:
};
module.exports = link;
-170
View File
@@ -1,170 +0,0 @@
var path = require('path');
var mout = require('mout');
var Q = require('q');
var Project = require('../core/Project');
var semver = require('../util/semver');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function list(logger, options, config) {
var project;
options = options || {};
// Make relative option true by default when used with paths
if (options.paths && options.relative == null) {
options.relative = true;
}
config = defaultConfig(config);
project = new Project(config, logger);
return project.getTree(options)
.spread(function (tree, flattened) {
// Relativize paths
// Also normalize paths on windows
project.walkTree(tree, function (node) {
if (node.missing) {
return;
}
if (options.relative) {
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
}
}, true);
// Note that we need to to parse the flattened tree because it might
// contain additional packages
mout.object.forOwn(flattened, function (node) {
if (node.missing) {
return;
}
if (options.relative) {
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
}
if (options.paths) {
node.canonicalDir = normalize(node.canonicalDir);
}
});
// Render paths?
if (options.paths) {
return paths(flattened);
}
// Do not check for new versions?
if (config.offline) {
return tree;
}
// Check for new versions
return checkVersions(project, tree, logger)
.then(function () {
return tree;
});
});
}
function checkVersions(project, tree, logger) {
var promises;
var nodes = [];
var repository = project.getPackageRepository();
// Gather all nodes, ignoring linked nodes
project.walkTree(tree, function (node) {
if (!node.linked) {
nodes.push(node);
}
}, true);
if (nodes.length) {
logger.info('check-new', 'Checking for new versions of the project dependencies..');
}
// Check for new versions for each node
promises = nodes.map(function (node) {
var target = node.endpoint.target;
return repository.versions(node.endpoint.source)
.then(function (versions) {
node.versions = versions;
// Do not check if node's target is not a valid semver one
if (versions.length && semver.validRange(target)) {
node.update = {
target: semver.maxSatisfying(versions, target),
latest: semver.maxSatisfying(versions, '*')
};
}
});
});
// Set the versions also for the root node
tree.versions = [];
return Q.all(promises);
}
function paths(flattened) {
var ret = {};
mout.object.forOwn(flattened, function (pkg, name) {
var main;
if (pkg.missing) {
return;
}
main = pkg.pkgMeta.main;
// If no main was specified, fallback to canonical dir
if (!main) {
ret[name] = pkg.canonicalDir;
return;
}
// Normalize main
if (typeof main === 'string') {
main = [main];
}
// Concatenate each main entry with the canonical dir
main = main.map(function (part) {
return normalize(path.join(pkg.canonicalDir, part).trim());
});
// If only one main file, use a string
// Otherwise use an array
ret[name] = main.length === 1 ? main[0] : main;
});
return ret;
}
function normalize(src) {
return src.replace(/\\/g, '/');
}
// -------------------
list.line = function (logger, argv) {
var options = list.options(argv);
return list(logger, options);
};
list.options = function (argv) {
return cli.readOptions({
'paths': { type: Boolean, shorthand: 'p' },
'relative': { type: Boolean, shorthand: 'r' }
}, argv);
};
list.completion = function () {
// TODO:
};
module.exports = list;
-42
View File
@@ -1,42 +0,0 @@
var Q = require('q');
var RegistryClient = require('bower-registry-client');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function lookup(logger, name, config) {
var registryClient;
config = defaultConfig(config);
config.cache = config.storage.registry;
registryClient = new RegistryClient(config, logger);
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
return !entry ? null : {
name: name,
url: entry && entry.url
};
});
}
// -------------------
lookup.line = function (logger, argv) {
var options = cli.readOptions(argv);
var name = options.argv.remain[1];
if (!name) {
return new Q();
} else {
return lookup(logger, name);
}
};
lookup.completion = function () {
// TODO:
};
module.exports = lookup;
-59
View File
@@ -1,59 +0,0 @@
var mout = require('mout');
var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function prune(logger, options, config) {
var project;
options = options || {};
config = defaultConfig(config);
project = new Project(config, logger);
return clean(project, options);
}
function clean(project, options, removed) {
removed = removed || {};
// Continually call clean until there is no more extraneous
// dependencies to remove
return project.getTree(options)
.spread(function (tree, flattened, extraneous) {
var names = extraneous.map(function (extra) {
return extra.endpoint.name;
});
// Uninstall extraneous
return project.uninstall(names, options)
.then(function (uninstalled) {
// Are we done?
if (!mout.object.size(uninstalled)) {
return removed;
}
// Not yet, recurse!
mout.object.mixIn(removed, uninstalled);
return clean(project, options, removed);
});
});
}
// -------------------
prune.line = function (logger, argv) {
var options = prune.options(argv);
return prune(logger, options);
};
prune.options = function (argv) {
return cli.readOptions({
'production': { type: Boolean, shorthand: 'p' },
}, argv);
};
prune.completion = function () {
// TODO:
};
module.exports = prune;
-123
View File
@@ -1,123 +0,0 @@
var mout = require('mout');
var Q = require('q');
var chalk = require('chalk');
var PackageRepository = require('../core/PackageRepository');
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');
var GitHubResolver = require('../core/resolvers/GitHubResolver');
function register(logger, name, url, config) {
var repository;
var registryClient;
var tracker;
var force;
config = defaultConfig(config);
force = config.force;
tracker = new Tracker(config);
// Bypass any cache
config.offline = false;
config.force = true;
// Trim name
name = name.trim();
return Q.try(function () {
// Verify name
// TODO: Verify with the new spec regexp?
if (!name) {
throw createError('Please type a name', 'EINVNAME');
}
// The public registry only allows git:// endpoints
// As such, we attempt to convert URLs as necessary
if (config.registry.register === Config.DEFAULT_REGISTRY) {
url = convertUrl(url, logger);
if (!mout.string.startsWith(url, 'git://')) {
throw createError('The registry only accepts URLs starting with git://', 'EINVFORMAT');
}
}
tracker.track('register');
// Attempt to resolve the package referenced by the URL to ensure
// everything is ok before registering
repository = new PackageRepository(config, logger);
return repository.fetch({ name: name, source: url, target: '*' });
})
.spread(function (canonicalDir, pkgMeta) {
if (pkgMeta.private) {
throw createError('The package you are trying to register is marked as private', 'EPRIV');
}
// If 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);
});
}
function convertUrl(url, logger) {
var pair;
var newUrl;
if (!mout.string.startsWith(url, 'git://')) {
// Convert GitHub ssh & https to git://
pair = GitHubResolver.getOrgRepoPair(url);
if (pair) {
newUrl = 'git://github.com/' + pair.org + '/' + pair.repo + '.git';
logger.warn('convert', 'Converted ' + url + ' to ' + newUrl);
}
}
return newUrl || url;
}
// -------------------
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 new Q();
} else {
return register(logger, name, url);
}
};
register.completion = function () {
// TODO:
};
module.exports = register;
-39
View File
@@ -1,39 +0,0 @@
var Q = require('q');
var RegistryClient = require('bower-registry-client');
var cli = require('../util/cli');
var Tracker = require('../util/analytics').Tracker;
var defaultConfig = require('../config');
function search(logger, name, config) {
var registryClient;
var tracker;
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) {
return Q.nfcall(registryClient.list.bind(registryClient));
// Otherwise search it
} else {
return Q.nfcall(registryClient.search.bind(registryClient), name);
}
}
// -------------------
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 () {
// TODO:
};
module.exports = search;
-124
View File
@@ -1,124 +0,0 @@
var mout = require('mout');
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(logger, names, options, config) {
var project;
var tracker;
options = options || {};
config = defaultConfig(config);
project = new Project(config, logger);
tracker = new Tracker(config);
tracker.trackNames('uninstall', names);
return project.getTree(options)
.spread(function (tree, flattened) {
// Uninstall nodes
return project.uninstall(names, options)
// Clean out non-shared uninstalled dependencies
.then(function (uninstalled) {
var names = Object.keys(uninstalled);
var children = [];
// Grab the dependencies of packages that were uninstalled
mout.object.forOwn(flattened, function (node) {
if (names.indexOf(node.endpoint.name) !== -1) {
children.push.apply(children, mout.object.keys(node.dependencies));
}
});
// Clean them!
return clean(project, children, uninstalled);
});
});
}
function clean(project, names, removed) {
removed = 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) {
if (names.indexOf(node.endpoint.name) !== -1) {
nodes.push(node);
}
});
// Walk the down the tree, gathering dependants of the packages
project.walkTree(tree, function (node, nodeName) {
if (names.indexOf(nodeName) !== -1) {
dependantsCounter[nodeName] = dependantsCounter[nodeName] || 0;
dependantsCounter[nodeName] += node.nrDependants;
}
}, true);
// Filter out those that have no dependants
nodes = nodes.filter(function (node) {
return !dependantsCounter[node.endpoint.name];
});
// Are we done?
if (!nodes.length) {
return Q.resolve(removed);
}
// Grab the nodes after filtering
names = nodes.map(function (node) {
return node.endpoint.name;
});
// Uninstall them
return project.uninstall(names)
// Clean out non-shared uninstalled dependencies
.then(function (uninstalled) {
var children;
mout.object.mixIn(removed, uninstalled);
// Grab the dependencies of packages that were uninstalled
children = [];
nodes.forEach(function (node) {
children.push.apply(children, mout.object.keys(node.dependencies));
});
// Recurse!
return clean(project, children, removed);
});
});
}
// -------------------
uninstall.line = function (logger, argv) {
var options = uninstall.options(argv);
var names = options.argv.remain.slice(1);
if (!names.length) {
return new Q();
} else {
return uninstall(logger, names, options);
}
};
uninstall.options = function (argv) {
return cli.readOptions({
'save': { type: Boolean, shorthand: 'S' },
'save-dev': { type: Boolean, shorthand: 'D' }
}, argv);
};
uninstall.completion = function () {
// TODO:
};
module.exports = uninstall;
-39
View File
@@ -1,39 +0,0 @@
var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function update(logger, names, options, config) {
var project;
options = options || {};
config = defaultConfig(config);
project = new Project(config, logger);
// If names is an empty array, null them
if (names && !names.length) {
names = null;
}
return project.update(names, options);
}
// -------------------
update.line = function (logger, argv) {
var options = update.options(argv);
var names = options.argv.remain.slice(1);
return update(logger, names, options);
};
update.options = function (argv) {
return cli.readOptions({
'force-latest': { type: Boolean, shorthand: 'F' },
'production': { type: Boolean, shorthand: 'p' }
}, argv);
};
update.completion = function () {
// TODO:
};
module.exports = update;
-131
View File
@@ -1,131 +0,0 @@
var semver = require('semver');
var which = require('which');
var fs = require('fs');
var path = require('path');
var Q = require('q');
var execFile = require('child_process').execFile;
var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
var createError = require('../util/createError');
function version(logger, versionArg, options, config) {
var project;
config = defaultConfig(config);
project = new Project(config, logger);
return bump(project, versionArg, options.message);
}
function bump(project, versionArg, message) {
var newVersion;
var doGitCommit = false;
return checkGit()
.then(function (hasGit) {
doGitCommit = hasGit;
})
.then(project.getJson.bind(project))
.then(function (json) {
newVersion = getNewVersion(json.version, versionArg);
json.version = newVersion;
})
.then(project.saveJson.bind(project))
.then(function () {
if (doGitCommit) {
return gitCommitAndTag(newVersion, message);
}
})
.then(function () {
console.log('v' + newVersion);
});
}
function getNewVersion(currentVersion, versionArg) {
var newVersion = semver.valid(versionArg);
if (!newVersion) {
newVersion = semver.inc(currentVersion, versionArg);
}
if (!newVersion) {
throw createError('Invalid version argument: `' + versionArg + '`. Usage: `bower version [<newversion> | major | minor | patch]`', 'EINVALIDVERSION');
}
if (currentVersion === newVersion) {
throw createError('Version not changed', 'EVERSIONNOTCHANGED');
}
return newVersion;
}
function checkGit() {
var gitDir = path.join(process.cwd(), '.git');
return Q.nfcall(fs.stat, gitDir)
.then(function (stat) {
if (stat.isDirectory()) {
return checkGitStatus();
}
return false;
}, function () {
//Ignore not found .git directory
return false;
});
}
function checkGitStatus() {
return Q.nfcall(which, 'git')
.fail(function (err) {
err.code = 'ENOGIT';
throw err;
})
.then(function () {
return Q.nfcall(execFile, 'git', ['status', '--porcelain'], {env: process.env});
})
.then(function (value) {
var stdout = value[0];
var lines = filterModifiedStatusLines(stdout);
if (lines.length) {
throw createError('Git working directory not clean.\n' + lines.join('\n'), 'EWORKINGDIRECTORYDIRTY');
}
return true;
});
}
function filterModifiedStatusLines(stdout) {
return stdout.trim().split('\n')
.filter(function (line) {
return line.trim() && !line.match(/^\?\? /);
}).map(function (line) {
return line.trim();
});
}
function gitCommitAndTag(newVersion, message) {
var tag = 'v' + newVersion;
message = message || tag;
message = message.replace(/%s/g, newVersion);
return Q.nfcall(execFile, 'git', ['add', 'bower.json'], {env: process.env})
.then(function () {
return Q.nfcall(execFile, 'git', ['commit', '-m', message], {env: process.env});
})
.then(function () {
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {env: process.env});
});
}
// -------------------
version.line = function (logger, argv) {
var options = version.options(argv);
return version(logger, options.argv.remain[1], options);
};
version.options = function (argv) {
return cli.readOptions({
'message': { type: String, shorthand: 'm'}
}, argv);
};
version.completion = function () {
// TODO:
};
module.exports = version;
-55
View File
@@ -1,55 +0,0 @@
var tty = require('tty');
var object = require('mout').object;
var bowerConfig = require('bower-config');
var cli = require('./util/cli');
var cachedConfigs = {};
function defaultConfig(config) {
config = config || {};
var cachedConfig = readCachedConfig(config.cwd || process.cwd());
return object.merge(cachedConfig, config);
}
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;
}
function resetCache () {
cachedConfigs = {};
}
module.exports = defaultConfig;
module.exports.reset = resetCache;
File diff suppressed because it is too large Load Diff
-219
View File
@@ -1,219 +0,0 @@
var mout = require('mout');
var Q = require('q');
var RegistryClient = require('bower-registry-client');
var ResolveCache = require('./ResolveCache');
var resolverFactory = require('./resolverFactory');
var createError = require('../util/createError');
function PackageRepository(config, logger) {
var registryOptions;
this._config = config;
this._logger = logger;
// Instantiate the registry
registryOptions = mout.object.deepMixIn({}, this._config);
registryOptions.cache = this._config.storage.registry;
this._registryClient = new RegistryClient(registryOptions, logger);
// Instantiate the resolve cache
this._resolveCache = new ResolveCache(this._config);
}
// -----------------
PackageRepository.prototype.fetch = function (decEndpoint) {
var logger;
var that = this;
var isTargetable;
var info = {
decEndpoint: decEndpoint
};
// Create a new logger that pipes everything to ours that will be
// used to fetch
logger = this._logger.geminate();
// Intercept all logs, adding additional information
logger.intercept(function (log) {
that._extendLog(log, info);
});
// Get the appropriate resolver
return resolverFactory(decEndpoint, this._config, logger, this._registryClient)
// Decide if we retrieve from the cache or not
// Also decide if we validate the cached entry or not
.then(function (resolver) {
info.resolver = resolver;
isTargetable = resolver.constructor.isTargetable;
if (!resolver.isCacheable()) {
return that._resolve(resolver, logger);
}
// If force flag is used, bypass cache, but write to cache anyway
if (that._config.force) {
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
return that._resolve(resolver, logger);
}
// Note that we use the resolver methods to query the
// cache because transformations/normalisations can occur
return that._resolveCache.retrieve(resolver.getSource(), resolver.getTarget())
// Decide if we can use the one from the resolve cache
.spread(function (canonicalDir, pkgMeta) {
// If there's no package in the cache
if (!canonicalDir) {
// And the offline flag is passed, error out
if (that._config.offline) {
throw createError('No cached version for ' + resolver.getSource() + '#' + resolver.getTarget(), 'ENOCACHE', {
resolver: resolver
});
}
// Otherwise, we have to resolve it
logger.info('not-cached', resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
return that._resolve(resolver, logger);
}
info.canonicalDir = canonicalDir;
info.pkgMeta = pkgMeta;
logger.info('cached', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
// If offline flag is used, use directly the cached one
if (that._config.offline) {
return [canonicalDir, pkgMeta, isTargetable];
}
// Otherwise check for new contents
logger.action('validate', (pkgMeta._release ? pkgMeta._release + ' against ': '') +
resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
return resolver.hasNew(canonicalDir, pkgMeta)
.then(function (hasNew) {
// If there are no new contents, resolve to
// the cached one
if (!hasNew) {
return [canonicalDir, pkgMeta, isTargetable];
}
// Otherwise resolve to the newest one
logger.info('new', 'version for ' + resolver.getSource() + '#' + resolver.getTarget());
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
return that._resolve(resolver, logger);
});
});
})
// If something went wrong, also extend the error
.fail(function (err) {
that._extendLog(err, info);
throw err;
});
};
PackageRepository.prototype.versions = function (source) {
// Resolve the source using the factory because the
// source can actually be a registry name
return resolverFactory.getConstructor(source, this._config, this._registryClient)
.spread(function (ConcreteResolver, source) {
// If offline, resolve using the cached versions
if (this._config.offline) {
return this._resolveCache.versions(source);
}
// Otherwise, fetch remotely
return ConcreteResolver.versions(source);
}.bind(this));
};
PackageRepository.prototype.eliminate = function (pkgMeta) {
return Q.all([
this._resolveCache.eliminate(pkgMeta),
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient), pkgMeta.name)
]);
};
PackageRepository.prototype.clear = function () {
return Q.all([
this._resolveCache.clear(),
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient))
]);
};
PackageRepository.prototype.reset = function () {
this._resolveCache.reset();
this._registryClient.resetCache();
};
PackageRepository.prototype.list = function () {
return this._resolveCache.list();
};
PackageRepository.prototype.getRegistryClient = function () {
return this._registryClient;
};
PackageRepository.prototype.getResolveCache = function () {
return this._resolveCache;
};
PackageRepository.clearRuntimeCache = function () {
ResolveCache.clearRuntimeCache();
RegistryClient.clearRuntimeCache();
resolverFactory.clearRuntimeCache();
};
// ---------------------
PackageRepository.prototype._resolve = function (resolver, logger) {
var that = this;
// Resolve the resolver
return resolver.resolve()
// Store in the cache
.then(function (canonicalDir) {
if (!resolver.isCacheable()) {
return canonicalDir;
}
return that._resolveCache.store(canonicalDir, resolver.getPkgMeta());
})
// Resolve promise with canonical dir and package meta
.then(function (dir) {
var pkgMeta = resolver.getPkgMeta();
logger.info('resolved', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
return [dir, pkgMeta, resolver.constructor.isTargetable()];
});
};
PackageRepository.prototype._extendLog = function (log, info) {
log.data = log.data || {};
// Store endpoint info in each log
if (info.decEndpoint) {
log.data.endpoint = mout.object.pick(info.decEndpoint, ['name', 'source', 'target']);
}
// Store the resolver info in each log
if (info.resolver) {
log.data.resolver = {
name: info.resolver.getName(),
source: info.resolver.getSource(),
target: info.resolver.getTarget()
};
}
// Store the canonical dir and its meta in each log
if (info.canonicalDir) {
log.data.canonicalDir = info.canonicalDir;
log.data.pkgMeta = info.pkgMeta;
}
return log;
};
module.exports = PackageRepository;
-829
View File
@@ -1,829 +0,0 @@
var glob = require('glob');
var path = require('path');
var fs = require('graceful-fs');
var Q = require('q');
var mout = require('mout');
var rimraf = require('rimraf');
var endpointParser = require('bower-endpoint-parser');
var Logger = require('bower-logger');
var Manager = require('./Manager');
var defaultConfig = require('../config');
var semver = require('../util/semver');
var md5 = require('../util/md5');
var createError = require('../util/createError');
var readJson = require('../util/readJson');
var validLink = require('../util/validLink');
var scripts = require('./scripts');
function Project(config, logger) {
// This is the only architecture component that ensures defaults
// 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 = defaultConfig(config);
this._logger = logger || new Logger();
this._manager = new Manager(this._config, this._logger);
this._options = {};
}
// -----------------
Project.prototype.install = function (decEndpoints, options, config) {
var that = this;
var targets = [];
var resolved = {};
var incompatibles = [];
// If already working, error out
if (this._working) {
return Q.reject(createError('Already working', 'EWORKING'));
}
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.incompatible) {
incompatibles.push(node);
} else if (node.missing || node.different || that._config.force) {
targets.push(node);
} else {
resolved[name] = node;
}
}, true);
// Add decomposed endpoints as targets
decEndpoints = decEndpoints || [];
decEndpoints.forEach(function (decEndpoint) {
// Mark as new so that a conflict for this target
// always require a choice
// Also allows for the target to be converted in case
// of being *
decEndpoint.newly = true;
targets.push(decEndpoint);
});
// 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) {
// Cycle through the specified endpoints
decEndpoints.forEach(function (decEndpoint) {
var jsonEndpoint;
jsonEndpoint = endpointParser.decomposed2json(decEndpoint);
if (that._options.save) {
that._json.dependencies = mout.object.mixIn(that._json.dependencies || {}, jsonEndpoint);
}
if (that._options.saveDev) {
that._json.devDependencies = mout.object.mixIn(that._json.devDependencies || {}, jsonEndpoint);
}
});
}
// Save JSON, might contain changes to dependencies and resolutions
return that.saveJson()
.then(function () {
return that._manager.postinstall(that._json).then(function () {
return installed;
});
});
})
.fin(function () {
that._installed = null;
that._working = false;
});
};
Project.prototype.update = function (names, options) {
var that = this;
var targets = [];
var resolved = {};
var incompatibles = [];
// If already working, error out
if (this._working) {
return Q.reject(createError('Already working', 'EWORKING'));
}
this._options = options || {};
this._working = true;
// Analyse the project
return this._analyse()
.spread(function (json, tree, flattened) {
// If no names were specified, update every package
if (!names) {
// Mark each root dependency as targets
that.walkTree(tree, function (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);
// Otherwise, selectively update the specified ones
} else {
// Error out if some of the specified names
// are not installed
names.forEach(function (name) {
if (!flattened[name]) {
throw createError('Package ' + name + ' is not installed', 'ENOTINS', {
name: name
});
}
});
// Add packages whose names are specified to be updated
that.walkTree(tree, function (node, name) {
if (names.indexOf(name) !== -1) {
// 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);
// Recover tree
that.walkTree(tree, function (node, name) {
if (node.missing || node.different) {
targets.push(node);
} else if (node.incompatible) {
incompatibles.push(node);
} else {
resolved[name] = node;
}
}, 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 that._manager.postinstall(that._json).then(function () {
return installed;
});
});
});
})
.fin(function () {
that._installed = null;
that._working = false;
});
};
Project.prototype.uninstall = function (names, options) {
var that = this;
var packages = {};
// If already working, error out
if (this._working) {
return Q.reject(createError('Already working', 'EWORKING'));
}
this._options = options || {};
this._working = true;
// Analyse the project
return this._analyse()
// Fill in the packages to be uninstalled
.spread(function (json, tree, flattened) {
var promise = Q.resolve();
names.forEach(function (name) {
var decEndpoint = flattened[name];
// Check if it is not installed
if (!decEndpoint || decEndpoint.missing) {
packages[name] = null;
return;
}
promise = promise
.then(function () {
var message;
var data;
var dependantsNames;
var dependants = [];
// Walk the down the tree, gathering dependants of the package
that.walkTree(tree, function (node, nodeName) {
if (name === nodeName) {
dependants.push.apply(dependants, mout.object.values(node.dependants));
}
}, true);
// Remove duplicates
dependants = mout.array.unique(dependants);
// Note that the root is filtered from the dependants
// as well as other dependants marked to be uninstalled
dependants = dependants.filter(function (dependant) {
return !dependant.root && names.indexOf(dependant.name) === -1;
});
// If the package has no dependants or the force config is enabled,
// mark it to be removed
if (!dependants.length || that._config.force) {
packages[name] = decEndpoint.canonicalDir;
return;
}
// Otherwise we need to figure it out if the user really wants to remove it,
// even with dependants
// As such we need to prompt the user with a meaningful message
dependantsNames = dependants.map(function (dep) { return dep.name; });
dependantsNames.sort(function (name1, name2) { return name1.localeCompare(name2); });
dependantsNames = mout.array.unique(dependantsNames);
dependants = dependants.map(function (dependant) { return that._manager.toData(dependant); });
message = dependantsNames.join(', ') + ' depends on ' + decEndpoint.name;
data = {
name: decEndpoint.name,
dependants: dependants
};
// If interactive is disabled, error out
if (!that._config.interactive) {
throw createError(message, 'ECONFLICT', {
data: data
});
}
that._logger.conflict('mutual', message, data);
// Prompt the user
return Q.nfcall(that._logger.prompt.bind(that._logger), {
type: 'confirm',
message: 'Continue anyway?',
default: true
})
.then(function (confirmed) {
// If the user decided to skip it, remove from the array so that it won't
// influence subsequent dependants
if (!confirmed) {
mout.array.remove(names, name);
} else {
packages[name] = decEndpoint.canonicalDir;
}
});
});
});
return promise;
})
// Remove packages
.then(function () {
return that._removePackages(packages);
})
.fin(function () {
that._installed = null;
that._working = false;
});
};
Project.prototype.getTree = function (options) {
this._options = options || {};
return this._analyse()
.spread(function (json, tree, flattened) {
var extraneous = [];
var additionalKeys = ['missing', 'extraneous', 'different', 'linked'];
// Convert tree
tree = this._manager.toData(tree, additionalKeys);
// Mark incompatibles
this.walkTree(tree, function (node) {
var version;
var target = node.endpoint.target;
if (node.pkgMeta && semver.validRange(target)) {
version = node.pkgMeta.version;
// Ignore if target is '*' and resolved to a non-semver release
if (!version && target === '*') {
return;
}
if (!version || !semver.satisfies(version, target)) {
node.incompatible = true;
}
}
}, true);
// Convert extraneous
mout.object.forOwn(flattened, function (pkg) {
if (pkg.extraneous) {
extraneous.push(this._manager.toData(pkg, additionalKeys));
}
}, this);
// Convert flattened
flattened = mout.object.map(flattened, function (node) {
return this._manager.toData(node, additionalKeys);
}, this);
return [tree, flattened, extraneous];
}.bind(this));
};
Project.prototype.walkTree = function (node, fn, onlyOnce) {
var result;
var dependencies;
var queue = mout.object.values(node.dependencies);
if (onlyOnce === true) {
onlyOnce = [];
}
while (queue.length) {
node = queue.shift();
result = fn(node, node.endpoint ? node.endpoint.name : node.name);
// Abort traversal if result is false
if (result === false) {
continue;
}
// Add dependencies to the queue
dependencies = mout.object.values(node.dependencies);
// If onlyOnce was true, do not add if already traversed
if (onlyOnce) {
dependencies = dependencies.filter(function (dependency) {
return !mout.array.find(onlyOnce, function (stacked) {
if (dependency.endpoint) {
return mout.object.equals(dependency.endpoint, stacked.endpoint);
}
return dependency.name === stacked.name &&
dependency.source === stacked.source &&
dependency.target === stacked.target;
});
});
onlyOnce.push.apply(onlyOnce, dependencies);
}
queue.unshift.apply(queue, dependencies);
}
};
Project.prototype.saveJson = function (forceCreate) {
var file;
var jsonStr = JSON.stringify(this._json, null, ' ') + '\n';
var jsonHash = md5(jsonStr);
// Save only if there's something different
if (jsonHash === this._jsonHash) {
return Q.resolve();
}
// Error out if the json file does not exist, unless force create
// is true
if (!this._jsonFile && !forceCreate) {
this._logger.warn('no-json', 'No bower.json file to save to, use bower init to create one');
return Q.resolve();
}
file = this._jsonFile || path.join(this._config.cwd, 'bower.json');
return Q.nfcall(fs.writeFile, file, jsonStr)
.then(function () {
this._jsonHash = jsonHash;
this._jsonFile = file;
return this._json;
}.bind(this));
};
Project.prototype.hasJson = function () {
return this._readJson()
.then(function (json) {
return json ? this._jsonFile : false;
}.bind(this));
};
Project.prototype.getJson = function () {
return this._readJson();
};
Project.prototype.getManager = function () {
return this._manager;
};
Project.prototype.getPackageRepository = function () {
return this._manager.getPackageRepository();
};
// -----------------
Project.prototype._analyse = function () {
return Q.all([
this._readJson(),
this._readInstalled(),
this._readLinks()
])
.spread(function (json, installed, links) {
var root;
var jsonCopy = mout.lang.deepClone(json);
root = {
name: json.name,
source: this._config.cwd,
target: json.version || '*',
pkgMeta: jsonCopy,
canonicalDir: this._config.cwd,
root: true
};
mout.object.mixIn(installed, links);
// Mix direct extraneous as dependencies
// (dependencies installed without --save/--save-dev)
jsonCopy.dependencies = jsonCopy.dependencies || {};
jsonCopy.devDependencies = jsonCopy.devDependencies || {};
mout.object.forOwn(installed, function (decEndpoint, key) {
var pkgMeta = decEndpoint.pkgMeta;
var isSaved = jsonCopy.dependencies[key] || jsonCopy.devDependencies[key];
// The _direct propery is saved by the manager when .newly is specified
// It may happen pkgMeta is undefined if package is uninstalled
if (!isSaved && pkgMeta && pkgMeta._direct) {
decEndpoint.extraneous = true;
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, installed, 'dependencies');
// Do the same for the dev dependencies
if (!this._options.production) {
this._restoreNode(root, installed, 'devDependencies');
}
// Restore the rest of the extraneous (not installed directly)
mout.object.forOwn(installed, function (decEndpoint, name) {
if (!decEndpoint.dependants) {
decEndpoint.extraneous = true;
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 installed[json.name];
return [json, root, installed];
}.bind(this));
};
Project.prototype._bootstrap = function (targets, resolved, incompatibles) {
var installed = mout.object.map(this._installed, function (decEndpoint) {
return decEndpoint.pkgMeta;
});
this._json.resolutions = this._json.resolutions || {};
// Configure the manager and kick in the resolve process
return this._manager
.configure({
targets: targets,
resolved: resolved,
incompatibles: incompatibles,
resolutions: this._json.resolutions,
installed: installed,
forceLatest: this._options.forceLatest
})
.resolve()
.then(function () {
// If the resolutions is empty, delete key
if (!mout.object.size(this._json.resolutions)) {
delete this._json.resolutions;
}
}.bind(this));
};
Project.prototype._readJson = function () {
var that = this;
if (this._json) {
return Q.resolve(this._json);
}
// Read local json
return this._json = readJson(this._config.cwd, {
assume: { name: path.basename(this._config.cwd) || 'root' }
})
.spread(function (json, deprecated, assumed) {
var jsonStr;
if (deprecated) {
that._logger.warn('deprecated', 'You are using the deprecated ' + deprecated + ' file');
}
if (!assumed) {
that._jsonFile = path.join(that._config.cwd, deprecated ? deprecated : 'bower.json');
}
jsonStr = JSON.stringify(json, null, ' ') + '\n';
that._jsonHash = md5(jsonStr);
return that._json = json;
});
};
Project.prototype._readInstalled = function () {
var componentsDir;
var that = this;
if (this._installed) {
return Q.resolve(this._installed);
}
// Gather all folders that are actual packages by
// looking for the package metadata file
componentsDir = path.join(this._config.cwd, this._config.directory);
return this._installed = Q.nfcall(glob, '*/.bower.json', {
cwd: componentsDir,
dot: true
})
.then(function (filenames) {
var promises;
var decEndpoints = {};
// Foreach bower.json found
promises = filenames.map(function (filename) {
var name = path.dirname(filename);
var metaFile = path.join(componentsDir, filename);
// Read package metadata
return readJson(metaFile)
.spread(function (pkgMeta) {
decEndpoints[name] = {
name: name,
source: pkgMeta._originalSource || pkgMeta._source,
target: pkgMeta._target,
canonicalDir: path.dirname(metaFile),
pkgMeta: pkgMeta
};
});
});
// Wait until all files have been read
// and resolve with the decomposed endpoints
return Q.all(promises)
.then(function () {
return that._installed = decEndpoints;
});
});
};
Project.prototype._readLinks = function () {
var componentsDir;
var that = this;
// Read directory, looking for links
componentsDir = path.join(this._config.cwd, this._config.directory);
return Q.nfcall(fs.readdir, componentsDir)
.then(function (filenames) {
var promises;
var decEndpoints = {};
promises = filenames.map(function (filename) {
var dir = path.join(componentsDir, filename);
// Filter only those that are valid links
return validLink(dir)
.spread(function (valid, err) {
var name;
if (!valid) {
if (err) {
that._logger.debug('read-link', 'Link ' + dir + ' is invalid', {
filename: dir,
error: err
});
}
return;
}
// Skip links to files (see #783)
if (!valid.isDirectory()) {
return;
}
name = path.basename(dir);
return readJson(dir, {
assume: { name: name }
})
.spread(function (json, deprecated) {
if (deprecated) {
that._logger.warn('deprecated', 'Package ' + name + ' is using the deprecated ' + deprecated);
}
json._direct = true; // Mark as a direct dep of root
decEndpoints[name] = {
name: name,
source: dir,
target: '*',
canonicalDir: dir,
pkgMeta: json,
linked: true
};
});
});
});
// Wait until all links have been read
// and resolve with the decomposed endpoints
return Q.all(promises)
.then(function () {
return decEndpoints;
});
// Ignore if folder does not exist
}, function (err) {
if (err.code !== 'ENOENT') {
throw err;
}
return {};
});
};
Project.prototype._removePackages = function (packages) {
var that = this;
var promises = [];
return scripts.preuninstall(that._config, that._logger, packages, that._installed, that._json)
.then(function () {
mout.object.forOwn(packages, function (dir, name) {
var promise;
// Delete directory
if (!dir) {
promise = Q.resolve();
that._logger.warn('not-installed', '\'' + name + '\'' + ' cannot be uninstalled as it is not currently installed', {
name: name
});
} else {
promise = Q.nfcall(rimraf, dir);
that._logger.action('uninstall', name, {
name: name,
dir: dir
});
}
// Remove from json only if successfully deleted
if (that._options.save && that._json.dependencies) {
promise = promise
.then(function () {
delete that._json.dependencies[name];
});
}
if (that._options.saveDev && that._json.devDependencies) {
promise = promise
.then(function () {
delete that._json.devDependencies[name];
});
}
promises.push(promise);
});
return Q.all(promises);
})
.then(function () {
return that.saveJson();
})
// Resolve with removed packages
.then(function () {
return mout.object.filter(packages, function (dir) {
return !!dir;
});
});
};
Project.prototype._restoreNode = function (node, flattened, jsonKey, processed) {
var deps;
// Do not restore if the node is missing
if (node.missing) {
return;
}
node.dependencies = node.dependencies || {};
node.dependants = node.dependants || {};
processed = processed || {};
// Only process deps that are not yet processed
deps = mout.object.filter(node.pkgMeta[jsonKey], function (value, key) {
return !processed[node.name + ':' + key];
});
mout.object.forOwn(deps, function (value, key) {
var local = flattened[key];
var json = endpointParser.json2decomposed(key, value);
var restored;
var compatible;
var originalSource;
// Check if the dependency is not installed
if (!local) {
flattened[key] = restored = json;
restored.missing = true;
// Even if it is installed, check if it's compatible
// Note that linked packages are interpreted as compatible
// This might change in the future: #673
} else {
compatible = local.linked || (!local.missing && json.target === local.pkgMeta._target);
if (!compatible) {
restored = json;
if (!local.missing) {
restored.pkgMeta = local.pkgMeta;
restored.canonicalDir = local.canonicalDir;
restored.incompatible = true;
} else {
restored.missing = true;
}
} else {
restored = local;
mout.object.mixIn(local, json);
}
// Check if source changed, marking as different if it did
// We only do this for direct root dependencies that are compatible
if (node.root && compatible) {
originalSource = mout.object.get(local, 'pkgMeta._originalSource');
if (originalSource && originalSource !== json.source) {
restored.different = true;
}
}
}
// Cross reference
node.dependencies[key] = restored;
processed[node.name + ':' + key] = true;
restored.dependants = restored.dependants || {};
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', processed);
// Do the same for the incompatible local package
if (local && restored !== local) {
this._restoreNode(local, flattened, 'dependencies', processed);
}
}, this);
};
module.exports = Project;
-406
View File
@@ -1,406 +0,0 @@
var fs = require('graceful-fs');
var path = require('path');
var mout = require('mout');
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');
var md5 = require('../util/md5');
function ResolveCache(config) {
// TODO: Make some config entries, such as:
// - Max MB
// - Max versions per source
// - Max MB per source
// - etc..
this._config = config;
this._dir = this._config.storage.packages;
this._lockDir = this._config.storage.packages;
mkdirp.sync(this._lockDir);
// Cache is stored/retrieved statically to ensure singularity
// among instances
this._cache = this.constructor._cache.get(this._dir);
if (!this._cache) {
this._cache = new LRU({
max: 100,
maxAge: 60 * 5 * 1000 // 5 minutes
});
this.constructor._cache.set(this._dir, this._cache);
}
// Ensure dir is created
mkdirp.sync(this._dir);
}
// -----------------
ResolveCache.prototype.retrieve = function (source, target) {
var sourceId = md5(source);
var dir = path.join(this._dir, sourceId);
var that = this;
target = target || '*';
return this._getVersions(sourceId)
.spread(function (versions) {
var suitable;
// If target is a semver, find a suitable version
if (semver.validRange(target)) {
suitable = semver.maxSatisfying(versions, target, true);
if (suitable) {
return suitable;
}
}
// If target is '*' check if there's a cached '_wildcard'
if (target === '*') {
return mout.array.find(versions, function (version) {
return version === '_wildcard';
});
}
// Otherwise check if there's an exact match
return mout.array.find(versions, function (version) {
return version === target;
});
})
.then(function (version) {
var canonicalDir;
if (!version) {
return [];
}
// Resolve with canonical dir and package meta
canonicalDir = path.join(dir, encodeURIComponent(version));
return that._readPkgMeta(canonicalDir)
.then(function (pkgMeta) {
return [canonicalDir, pkgMeta];
}, function () {
// If there was an error, invalidate the in-memory cache,
// delete the cached package and try again
that._cache.del(sourceId);
return Q.nfcall(rimraf, canonicalDir)
.then(function () {
return that.retrieve(source, target);
});
});
});
};
ResolveCache.prototype.store = function (canonicalDir, pkgMeta) {
var sourceId;
var release;
var dir;
var pkgLock;
var promise;
var that = this;
promise = pkgMeta ? Q.resolve(pkgMeta) : this._readPkgMeta(canonicalDir);
return promise
.then(function (pkgMeta) {
sourceId = md5(pkgMeta._source);
release = that._getPkgRelease(pkgMeta);
dir = path.join(that._dir, sourceId, release);
pkgLock = path.join(that._lockDir, sourceId + '-' + release + '.lock');
// Check if destination directory exists to prevent issuing lock at all times
return Q.nfcall(fs.stat, dir)
.fail(function (err) {
var lockParams = { wait: 250, retries: 25, stale: 60000 };
return Q.nfcall(lockFile.lock, pkgLock, lockParams).then(function () {
// Ensure other process didn't start copying files before lock was created
return Q.nfcall(fs.stat, dir)
.fail(function (err) {
// If stat fails, it is expected to return ENOENT
if (err.code !== 'ENOENT') {
throw err;
}
// Create missing directory and copy files there
return Q.nfcall(mkdirp, path.dirname(dir)).then(function () {
return Q.nfcall(fs.rename, canonicalDir, dir)
.fail(function (err) {
// If error is EXDEV it means that we are trying to rename
// across different drives, so we copy and remove it instead
if (err.code !== 'EXDEV') {
throw err;
}
return copy.copyDir(canonicalDir, dir);
});
});
});
}).finally(function () {
lockFile.unlockSync(pkgLock);
});
}).finally(function () {
// Ensure no tmp dir is left on disk.
return Q.nfcall(rimraf, canonicalDir);
});
})
.then(function () {
var versions = that._cache.get(sourceId);
// Add it to the in memory cache
// and sort the versions afterwards
if (versions && versions.indexOf(release) === -1) {
versions.push(release);
that._sortVersions(versions);
}
// Resolve with the final location
return dir;
});
};
ResolveCache.prototype.eliminate = function (pkgMeta) {
var sourceId = md5(pkgMeta._source);
var release = this._getPkgRelease(pkgMeta);
var dir = path.join(this._dir, sourceId, release);
var that = this;
return Q.nfcall(rimraf, dir)
.then(function () {
var versions = that._cache.get(sourceId) || [];
mout.array.remove(versions, release);
// If this was the last package in the cache,
// delete the parent folder (source)
// For extra security, check against the file system
// if this was really the last package
if (!versions.length) {
that._cache.del(sourceId);
return that._getVersions(sourceId)
.spread(function (versions) {
if (!versions.length) {
// Do not keep in-memory cache if it's completely
// empty
that._cache.del(sourceId);
return Q.nfcall(rimraf, path.dirname(dir));
}
});
}
});
};
ResolveCache.prototype.clear = function () {
return Q.nfcall(rimraf, this._dir)
.then(function () {
return Q.nfcall(fs.mkdir, this._dir);
}.bind(this))
.then(function () {
this._cache.reset();
}.bind(this));
};
ResolveCache.prototype.reset = function () {
this._cache.reset();
return this;
};
ResolveCache.prototype.versions = function (source) {
var sourceId = md5(source);
return this._getVersions(sourceId)
.spread(function (versions) {
return versions.filter(function (version) {
return semver.valid(version);
});
});
};
ResolveCache.prototype.list = function () {
var promises;
var dirs = [];
var that = this;
// Get the list of directories
return Q.nfcall(fs.readdir, this._dir)
.then(function (sourceIds) {
promises = sourceIds.map(function (sourceId) {
return Q.nfcall(fs.readdir, path.join(that._dir, sourceId))
.then(function (versions) {
versions.forEach(function (version) {
var dir = path.join(that._dir, sourceId, version);
dirs.push(dir);
});
}, function (err) {
// Ignore lurking files, e.g.: .DS_Store if the user
// has navigated throughout the cache
if (err.code === 'ENOTDIR' && err.path) {
return Q.nfcall(rimraf, err.path);
}
throw err;
});
});
return Q.all(promises);
})
// Read every package meta
.then(function () {
promises = dirs.map(function (dir) {
return that._readPkgMeta(dir)
.then(function (pkgMeta) {
return {
canonicalDir: dir,
pkgMeta: pkgMeta
};
}, function () {
// If it fails to read, invalidate the in memory
// cache for the source and delete the entry directory
var sourceId = path.basename(path.dirname(dir));
that._cache.del(sourceId);
return Q.nfcall(rimraf, dir);
});
});
return Q.all(promises);
})
// Sort by name ASC & release ASC
.then(function (entries) {
// Ignore falsy entries due to errors reading
// package metas
entries = entries.filter(function (entry) {
return !!entry;
});
return entries.sort(function (entry1, entry2) {
var pkgMeta1 = entry1.pkgMeta;
var pkgMeta2 = entry2.pkgMeta;
var comp = pkgMeta1.name.localeCompare(pkgMeta2.name);
// Sort by name
if (comp) {
return comp;
}
// Sort by version
if (pkgMeta1.version && pkgMeta2.version) {
return semver.compare(pkgMeta1.version, pkgMeta2.version);
}
if (pkgMeta1.version) {
return -1;
}
if (pkgMeta2.version) {
return 1;
}
// Sort by target
return pkgMeta1._target.localeCompare(pkgMeta2._target);
});
});
};
// ------------------------
ResolveCache.clearRuntimeCache = function () {
// Note that _cache refers to the static _cache variable
// that holds other caches per dir!
// Do not confuse it with the instance cache
// Clear cache of each directory
this._cache.forEach(function (cache) {
cache.reset();
});
// Clear root cache
this._cache.reset();
};
// ------------------------
ResolveCache.prototype._getPkgRelease = function (pkgMeta) {
var release = pkgMeta.version || (pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
// Encode some dangerous chars such as / and \
release = encodeURIComponent(release);
return release;
};
ResolveCache.prototype._readPkgMeta = function (dir) {
var filename = path.join(dir, '.bower.json');
return readJson(filename)
.spread(function (json) {
return json;
});
};
ResolveCache.prototype._getVersions = function (sourceId) {
var dir;
var versions = this._cache.get(sourceId);
var that = this;
if (versions) {
return Q.resolve([versions, true]);
}
dir = path.join(this._dir, sourceId);
return Q.nfcall(fs.readdir, dir)
.then(function (versions) {
// Sort and cache in memory
that._sortVersions(versions);
versions = versions.map(decodeURIComponent);
that._cache.set(sourceId, versions);
return [versions, false];
}, function (err) {
// If the directory does not exists, resolve
// as an empty array
if (err.code === 'ENOENT') {
versions = [];
that._cache.set(sourceId, versions);
return [versions, false];
}
throw err;
});
};
ResolveCache.prototype._sortVersions = function (versions) {
// Sort DESC
versions.sort(function (version1, version2) {
var validSemver1 = semver.valid(version1);
var validSemver2 = semver.valid(version2);
// If both are semvers, compare them
if (validSemver1 && validSemver2) {
return semver.rcompare(version1, version2);
}
// If one of them are semvers, give higher priority
if (validSemver1) {
return -1;
}
if (validSemver2) {
return 1;
}
// Otherwise they are considered equal
return 0;
});
};
// ------------------------
ResolveCache._cache = new LRU({
max: 5,
maxAge: 60 * 30 * 1000 // 30 minutes
});
module.exports = ResolveCache;
-174
View File
@@ -1,174 +0,0 @@
var Q = require('q');
var fs = require('graceful-fs');
var path = require('path');
var mout = require('mout');
var resolvers = require('./resolvers');
var createError = require('../util/createError');
function createInstance(decEndpoint, config, logger, registryClient) {
return getConstructor(decEndpoint.source, config, registryClient)
.spread(function (ConcreteResolver, source, fromRegistry) {
var decEndpointCopy = mout.object.pick(decEndpoint, ['name', 'target']);
decEndpointCopy.source = source;
// Signal if it was fetched from the registry
if (fromRegistry) {
decEndpoint.registry = true;
// If no name was specified, assume the name from the registry
if (!decEndpointCopy.name) {
decEndpointCopy.name = decEndpoint.name = decEndpoint.source;
}
}
return new ConcreteResolver(decEndpointCopy, config, logger);
});
}
function getConstructor(source, config, registryClient) {
var absolutePath,
promise;
// Git case: git git+ssh, git+http, git+https
// .git at the end (probably ssh shorthand)
// git@ at the start
if (/^git(\+(ssh|https?))?:\/\//i.test(source) || /\.git\/?$/i.test(source) || /^git@/i.test(source)) {
source = source.replace(/^git\+/, '');
return Q.fcall(function () {
// If it's a GitHub repository, return the specialized resolver
if (resolvers.GitHub.getOrgRepoPair(source)) {
return [resolvers.GitHub, source];
}
return [resolvers.GitRemote, source];
});
}
// SVN case: svn, svn+ssh, svn+http, svn+https, svn+file
if (/^svn(\+(ssh|https?|file))?:\/\//i.test(source)) {
return Q.fcall(function () {
return [resolvers.Svn, source];
});
}
// URL case
if (/^https?:\/\//i.exec(source)) {
return Q.fcall(function () {
return [resolvers.Url, source];
});
}
// Below we try a series of async tests to guess the type of resolver to use
// If a step was unable to guess the resolver, it throws an error
// If a step was able to guess the resolver, it resolves with a function
// That function returns a promise that will resolve with the concrete type
// If source is ./ or ../ or an absolute path
absolutePath = path.resolve(config.cwd, source);
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()) {
return function () {
return Q.resolve([resolvers.GitFs, absolutePath]);
};
}
throw new Error('Not a Git repository');
})
// If not, check if source is a valid Subversion repository
.fail(function () {
return Q.nfcall(fs.stat, path.join(absolutePath, '.svn'))
.then(function (stats) {
if (stats.isDirectory()) {
return function () {
return Q.resolve([resolvers.Svn, absolutePath]);
};
}
throw new Error('Not a Subversion repository');
});
})
// If not, check if source is a valid file/folder
.fail(function () {
return Q.nfcall(fs.stat, absolutePath)
.then(function () {
return function () {
return Q.resolve([resolvers.Fs, absolutePath]);
};
});
});
} else {
promise = Q.reject(new Error('Not an absolute or relative file'));
}
return promise
// Check if is a shorthand and expand it
.fail(function (err) {
var parts;
// Skip ssh and/or URL with auth
if (/[:@]/.test(source)) {
throw err;
}
// Ensure exactly only one "/"
parts = source.split('/');
if (parts.length === 2) {
source = mout.string.interpolate(config.shorthandResolver, {
shorthand: source,
owner: parts[0],
package: parts[1]
});
return function () {
return getConstructor(source, config, registryClient);
};
}
throw err;
})
// As last resort, we try the registry
.fail(function (err) {
if (!registryClient) {
throw err;
}
return function () {
return Q.nfcall(registryClient.lookup.bind(registryClient), source)
.then(function (entry) {
if (!entry) {
throw createError('Package ' + source + ' not found', 'ENOTFOUND');
}
// TODO: Handle entry.type.. for now it's only 'alias'
// When we got published packages, this needs to be adjusted
source = entry.url;
return getConstructor(source, config, registryClient)
.spread(function (ConcreteResolver, source) {
return [ConcreteResolver, source, true];
});
});
};
})
// If we got the function, simply call and return
.then(function (func) {
return func();
// Finally throw a meaningful error
}, function () {
throw createError('Could not find appropriate resolver for ' + source, 'ENORESOLVER');
});
}
function clearRuntimeCache() {
mout.object.values(resolvers).forEach(function (ConcreteResolver) {
ConcreteResolver.clearRuntimeCache();
});
}
module.exports = createInstance;
module.exports.getConstructor = getConstructor;
module.exports.clearRuntimeCache = clearRuntimeCache;
@@ -1,143 +0,0 @@
var util = require('util');
var fs = require('graceful-fs');
var path = require('path');
var mout = require('mout');
var Q = require('q');
var junk = require('junk');
var Resolver = require('./Resolver');
var copy = require('../../util/copy');
var extract = require('../../util/extract');
var createError = require('../../util/createError');
function FsResolver(decEndpoint, config, logger) {
Resolver.call(this, decEndpoint, config, logger);
// Ensure absolute path
this._source = path.resolve(this._config.cwd, this._source);
// If target was specified, simply reject the promise
if (this._target !== '*') {
throw createError('File system sources can\'t resolve targets', 'ENORESTARGET');
}
// If the name was guessed
if (this._guessedName) {
// Remove extension
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
}
}
util.inherits(FsResolver, Resolver);
mout.object.mixIn(FsResolver, Resolver);
// -----------------
FsResolver.isTargetable = function () {
return false;
};
// TODO: Should we store latest mtimes in the resolution and compare?
// This would be beneficial when copying big files/folders
// TODO: There's room for improvement by using streams if the source
// is an archive file, by piping read stream to the zip extractor
// This will likely increase the complexity of code but might worth it
FsResolver.prototype._resolve = function () {
return this._copy()
.then(this._extract.bind(this))
.then(this._rename.bind(this));
};
// -----------------
FsResolver.prototype._copy = function () {
var that = this;
return Q.nfcall(fs.stat, this._source)
.then(function (stat) {
var dst;
var copyOpts;
var promise;
that._sourceStat = stat;
copyOpts = { mode: stat.mode };
// If it's a folder
if (stat.isDirectory()) {
dst = that._tempDir;
// Read the bower.json inside the folder, so that we
// copy only the necessary files if it has ignore specified
promise = that._readJson(that._source)
.then(function (json) {
copyOpts.ignore = json.ignore;
return copy.copyDir(that._source, dst, copyOpts);
})
.then(function () {
// Resolve to null because it's a dir
return;
});
// Else it's a file
} else {
dst = path.join(that._tempDir, path.basename(that._source));
promise = copy.copyFile(that._source, dst, copyOpts)
.then(function () {
return dst;
});
}
that._logger.action('copy', that._source, {
src: that._source,
dst: dst
});
return promise;
});
};
FsResolver.prototype._extract = function (file) {
if (!file || !extract.canExtract(file)) {
return Q.resolve();
}
this._logger.action('extract', path.basename(this._source), {
archive: file,
to: this._tempDir
});
return extract(file, this._tempDir);
};
FsResolver.prototype._rename = function () {
return Q.nfcall(fs.readdir, this._tempDir)
.then(function (files) {
var file;
var oldPath;
var newPath;
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
// Only rename if there's only one file and it's not the json
if (files.length === 1 && !/^(bower|component)\.json$/.test(files[0])) {
file = files[0];
this._singleFile = 'index' + path.extname(file);
oldPath = path.join(this._tempDir, file);
newPath = path.join(this._tempDir, this._singleFile);
return Q.nfcall(fs.rename, oldPath, newPath);
}
}.bind(this));
};
FsResolver.prototype._savePkgMeta = function (meta) {
// Store main if is a single file
if (this._singleFile) {
meta.main = this._singleFile;
}
return Resolver.prototype._savePkgMeta.call(this, meta);
};
module.exports = FsResolver;
@@ -1,77 +0,0 @@
var util = require('util');
var Q = require('q');
var mout = require('mout');
var path = require('path');
var GitResolver = require('./GitResolver');
var copy = require('../../util/copy');
var cmd = require('../../util/cmd');
function GitFsResolver(decEndpoint, config, logger) {
GitResolver.call(this, decEndpoint, config, logger);
// Ensure absolute path
this._source = path.resolve(this._config.cwd, this._source);
}
util.inherits(GitFsResolver, GitResolver);
mout.object.mixIn(GitFsResolver, GitResolver);
// -----------------
// Override the checkout function to work with the local copy
GitFsResolver.prototype._checkout = function () {
var resolution = this._resolution;
// The checkout process could be similar to the GitRemoteResolver by prepending file:// to the source
// But from my performance measures, it's faster to copy the folder and just checkout in there
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
// Copy files to the temporary directory first
return this._copy()
.then(cmd.bind(cmd, 'git', ['checkout', '-f', resolution.tag || resolution.branch || resolution.commit], { cwd: this._tempDir }))
// Cleanup unstaged files
.then(cmd.bind(cmd, 'git', ['clean', '-f', '-d'], { cwd: this._tempDir }));
};
GitFsResolver.prototype._copy = function () {
return copy.copyDir(this._source, this._tempDir);
};
// -----------------
// Grab refs locally
GitFsResolver.refs = function (source) {
var value;
// TODO: Normalize source because of the various available protocols?
value = this._cache.refs.get(source);
if (value) {
return Q.resolve(value);
}
value = cmd('git', ['show-ref', '--tags', '--heads'], { cwd : source })
.spread(function (stdout) {
var refs;
refs = stdout.toString()
.trim() // Trim trailing and leading spaces
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
.split(/[\r\n]+/); // Split lines into an array
// Update the refs with the actual refs
this._cache.refs.set(source, refs);
return refs;
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.refs.set(source, value);
return value;
};
module.exports = GitFsResolver;
@@ -1,150 +0,0 @@
var util = require('util');
var path = require('path');
var mout = require('mout');
var GitRemoteResolver = require('./GitRemoteResolver');
var download = require('../../util/download');
var extract = require('../../util/extract');
var createError = require('../../util/createError');
function GitHubResolver(decEndpoint, config, logger) {
var pair;
GitRemoteResolver.call(this, decEndpoint, config, logger);
// Grab the org/repo
// /xxxxx/yyyyy.git or :xxxxx/yyyyy.git (.git is optional)
pair = GitHubResolver.getOrgRepoPair(this._source);
if (!pair) {
throw createError('Invalid GitHub URL', 'EINVEND', {
details: this._source + ' does not seem to be a valid GitHub URL'
});
}
this._org = pair.org;
this._repo = pair.repo;
// Ensure trailing for all protocols
if (!mout.string.endsWith(this._source, '.git')) {
this._source += '.git';
}
// Check if it's public
this._public = mout.string.startsWith(this._source, 'git://');
// Use https:// rather than git:// if on a proxy
if (this._config.proxy || this._config.httpsProxy) {
this._source = this._source.replace('git://', 'https://');
}
// Enable shallow clones for GitHub repos
this._shallowClone = true;
}
util.inherits(GitHubResolver, GitRemoteResolver);
mout.object.mixIn(GitHubResolver, GitRemoteResolver);
// -----------------
GitHubResolver.prototype._checkout = function () {
// Only fully works with public repositories and tags
// Could work with https/ssh protocol but not with 100% certainty
if (!this._public || !this._resolution.tag) {
return GitRemoteResolver.prototype._checkout.call(this);
}
var msg;
var tarballUrl = 'https://github.com/' + this._org + '/' + this._repo + '/archive/' + this._resolution.tag + '.tar.gz';
var file = path.join(this._tempDir, 'archive.tar.gz');
var reqHeaders = {};
var that = this;
if (this._config.userAgent) {
reqHeaders['User-Agent'] = this._config.userAgent;
}
this._logger.action('download', tarballUrl, {
url: that._source,
to: file
});
// Download tarball
return download(tarballUrl, file, {
proxy: this._config.httpsProxy,
strictSSL: this._config.strictSsl,
timeout: this._config.timeout,
headers: reqHeaders
})
.progress(function (state) {
// Retry?
if (state.retry) {
msg = 'Download of ' + tarballUrl + ' failed with ' + state.error.code + ', ';
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
that._logger.debug('error', state.error.message, { error: state.error });
return that._logger.warn('retry', msg);
}
// Progress
msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
if (state.total) {
msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
msg += state.percent + '%';
}
that._logger.info('progress', msg);
})
.then(function () {
// Extract archive
that._logger.action('extract', path.basename(file), {
archive: file,
to: that._tempDir
});
return extract(file, that._tempDir)
// Fallback to standard git clone if extraction failed
.fail(function (err) {
msg = 'Decompression of ' + path.basename(file) + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
msg += 'trying with git..';
that._logger.debug('error', err.message, { error: err });
that._logger.warn('retry', msg);
return that._cleanTempDir()
.then(GitRemoteResolver.prototype._checkout.bind(that));
});
// Fallback to standard git clone if download failed
}, function (err) {
msg = 'Download of ' + tarballUrl + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
msg += 'trying with git..';
that._logger.debug('error', err.message, { error: err });
that._logger.warn('retry', msg);
return that._cleanTempDir()
.then(GitRemoteResolver.prototype._checkout.bind(that));
});
};
GitHubResolver.prototype._savePkgMeta = function (meta) {
// Set homepage if not defined
if (!meta.homepage) {
meta.homepage = 'https://github.com/' + this._org + '/' + this._repo;
}
return GitRemoteResolver.prototype._savePkgMeta.call(this, meta);
};
// ----------------
GitHubResolver.getOrgRepoPair = function (url) {
var match;
match = url.match(/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i);
if (!match) {
return null;
}
return {
org: match[1],
repo: match[2]
};
};
module.exports = GitHubResolver;
@@ -1,201 +0,0 @@
var util = require('util');
var url = require('url');
var Q = require('q');
var mout = require('mout');
var LRU = require('lru-cache');
var GitResolver = require('./GitResolver');
var cmd = require('../../util/cmd');
function GitRemoteResolver(decEndpoint, config, logger) {
GitResolver.call(this, decEndpoint, config, logger);
if (!mout.string.startsWith(this._source, 'file://')) {
// Trim trailing slashes
this._source = this._source.replace(/\/+$/, '');
}
// If the name was guessed, remove the trailing .git
if (this._guessedName && mout.string.endsWith(this._name, '.git')) {
this._name = this._name.slice(0, -4);
}
// Get the host of this source
if (!/:\/\//.test(this._source)) {
this._host = url.parse('ssh://' + this._source).host;
} else {
this._host = url.parse(this._source).host;
}
// Disable shallow clones
this._shallowClone = false;
}
util.inherits(GitRemoteResolver, GitResolver);
mout.object.mixIn(GitRemoteResolver, GitResolver);
// -----------------
GitRemoteResolver.prototype._checkout = function () {
var promise;
var timer;
var reporter;
var that = this;
var resolution = this._resolution;
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
// If resolution is a commit, we need to clone the entire repo and check it out
// Because a commit is not a named ref, there's no better solution
if (resolution.type === 'commit') {
promise = this._slowClone(resolution);
// Otherwise we are checking out a named ref so we can optimize it
} else {
promise = this._fastClone(resolution);
}
// Throttle the progress reporter to 1 time each sec
reporter = mout.fn.throttle(function (data) {
var lines;
lines = data.split(/[\r\n]+/);
lines.forEach(function (line) {
if (/\d{1,3}\%/.test(line)) {
// TODO: There are some strange chars that appear once in a while (\u001b[K)
// Trim also those?
that._logger.info('progress', line.trim());
}
});
}, 1000);
// Start reporting progress after a few seconds
timer = setTimeout(function () {
promise.progress(reporter);
}, 8000);
return promise
// Add additional proxy information to the error if necessary
.fail(function (err) {
that._suggestProxyWorkaround(err);
throw err;
})
// Clear timer at the end
.fin(function () {
clearTimeout(timer);
reporter.cancel();
});
};
GitRemoteResolver.prototype._findResolution = function (target) {
var that = this;
// Override this function to include a meaningful message related to proxies
// if necessary
return GitResolver.prototype._findResolution.call(this, target)
.fail(function (err) {
that._suggestProxyWorkaround(err);
throw err;
});
};
// ------------------------------
GitRemoteResolver.prototype._slowClone = function (resolution) {
return cmd('git', ['clone', this._source, this._tempDir, '--progress'])
.then(cmd.bind(cmd, 'git', ['checkout', resolution.commit], { cwd: this._tempDir }));
};
GitRemoteResolver.prototype._fastClone = function (resolution) {
var branch,
args,
that = this;
branch = resolution.tag || resolution.branch;
args = ['clone', this._source, '-b', branch, '--progress', '.'];
// If the host does not support shallow clones, we don't use --depth=1
if (this._shallowClone && !GitRemoteResolver._noShallow.get(this._host)) {
args.push('--depth', 1);
}
return cmd('git', args, { cwd: this._tempDir })
.spread(function (stdout, stderr) {
// Only after 1.7.10 --branch accepts tags
// Detect those cases and inform the user to update git otherwise it's
// a lot slower than newer versions
if (!/branch .+? not found/i.test(stderr)) {
return;
}
that._logger.warn('old-git', 'It seems you are using an old version of git, it will be slower and propitious to errors!');
return cmd('git', ['checkout', resolution.commit], { cwd: that._tempDir });
}, function (err) {
// Some git servers do not support shallow clones
// When that happens, we mark this host and try again
if (!GitRemoteResolver._noShallow.has(that._source) &&
err.details &&
/(rpc failed|shallow|--depth)/i.test(err.details)
) {
GitRemoteResolver._noShallow.set(that._host, true);
return that._fastClone(resolution);
}
throw err;
});
};
GitRemoteResolver.prototype._suggestProxyWorkaround = function (err) {
if ((this._config.proxy || this._config.httpsProxy) &&
mout.string.startsWith(this._source, 'git://') &&
err.code === 'ECMDERR' && err.details
) {
err.details = err.details.trim();
err.details += '\n\nWhen under a proxy, you must configure git to use https:// instead of git://.';
err.details += '\nYou can configure it for every endpoint or for this specific host as follows:';
err.details += '\ngit config --global url."https://".insteadOf git://';
err.details += '\ngit config --global url."https://' + this._host + '".insteadOf git://' + this._host;
err.details += 'Ignore this suggestion if you already have this configured.';
}
};
// ------------------------------
// Grab refs remotely
GitRemoteResolver.refs = function (source) {
var value;
// TODO: Normalize source because of the various available protocols?
value = this._cache.refs.get(source);
if (value) {
return Q.resolve(value);
}
// Store the promise in the refs object
value = cmd('git', ['ls-remote', '--tags', '--heads', source])
.spread(function (stdout) {
var refs;
refs = stdout.toString()
.trim() // Trim trailing and leading spaces
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
.split(/[\r\n]+/); // Split lines into an array
// Update the refs with the actual refs
this._cache.refs.set(source, refs);
return refs;
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.refs.set(source, value);
return value;
};
// Store hosts that do not support shallow clones here
GitRemoteResolver._noShallow = new LRU({ max: 50, maxAge: 5 * 60 * 1000 });
module.exports = GitRemoteResolver;
@@ -1,387 +0,0 @@
var util = require('util');
var path = require('path');
var Q = require('q');
var chmodr = require('chmodr');
var rimraf = require('rimraf');
var mkdirp = require('mkdirp');
var which = require('which');
var LRU = require('lru-cache');
var mout = require('mout');
var Resolver = require('./Resolver');
var semver = require('../../util/semver');
var createError = require('../../util/createError');
var hasGit;
// Check if git is installed
try {
which.sync('git');
hasGit = true;
} catch (ex) {
hasGit = false;
}
function GitResolver(decEndpoint, config, logger) {
// Set template dir to the empty directory so that user templates are not run
// This environment variable is not multiple config aware but it's not documented
// anyway
mkdirp.sync(config.storage.empty);
process.env.GIT_TEMPLATE_DIR = config.storage.empty;
Resolver.call(this, decEndpoint, config, logger);
if (!hasGit) {
throw createError('git is not installed or not in the PATH', 'ENOGIT');
}
}
util.inherits(GitResolver, Resolver);
mout.object.mixIn(GitResolver, Resolver);
// -----------------
GitResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
var oldResolution = pkgMeta._resolution || {};
return this._findResolution()
.then(function (resolution) {
// Check if resolution types are different
if (oldResolution.type !== resolution.type) {
return true;
}
// If resolved to a version, there is new content if the tags are not equal
if (resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag)) {
return true;
}
// As last check, we compare both commit hashes
return resolution.commit !== oldResolution.commit;
});
};
GitResolver.prototype._resolve = function () {
var that = this;
return this._findResolution()
.then(function () {
return that._checkout()
// Always run cleanup after checkout to ensure that .git is removed!
// If it's not removed, problems might arise when the "tmp" module attempts
// to delete the temporary folder
.fin(function () {
return that._cleanup();
});
});
};
// -----------------
// Abstract functions that should be implemented by concrete git resolvers
GitResolver.prototype._checkout = function () {
throw new Error('_checkout not implemented');
};
GitResolver.refs = function (source) {
throw new Error('refs not implemented');
};
// -----------------
GitResolver.prototype._findResolution = function (target) {
var err;
var self = this.constructor;
var that = this;
target = target || this._target || '*';
// Target is a commit, so it's a stale target (not a moving target)
// There's nothing to do in this case
if ((/^[a-f0-9]{40}$/).test(target)) {
this._resolution = { type: 'commit', commit: target };
return Q.resolve(this._resolution);
}
// Target is a range/version
if (semver.validRange(target)) {
return self.versions(this._source, true)
.then(function (versions) {
var versionsArr,
version,
index;
versionsArr = versions.map(function (obj) { return obj.version; });
// If there are no tags and target is *,
// fallback to the latest commit on master
if (!versions.length && target === '*') {
return that._findResolution('master');
}
versionsArr = versions.map(function (obj) { return obj.version; });
// Find a satisfying version, enabling strict match so that pre-releases
// have lower priority over normal ones when target is *
index = semver.maxSatisfyingIndex(versionsArr, target, true);
if (index !== -1) {
version = versions[index];
return that._resolution = { type: 'version', tag: version.tag, commit: version.commit };
}
// Check if there's an exact branch/tag with this name as last resort
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
details: !versions.length ?
'No versions found in ' + that._source :
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
});
});
});
}
// Otherwise, target is either a tag or a branch
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
if ((/^[a-f0-9]{4,40}$/).test(target)) {
if (target.length < 12) {
that._logger.warn(
'short-sha',
'Consider using longer commit SHA to avoid conflicts'
);
}
that._resolution = { type: 'commit', commit: target };
return that._resolution;
}
branches = Object.keys(branches);
tags = Object.keys(tags);
err = createError('Tag/branch ' + target + ' does not exist', 'ENORESTARGET');
err.details = !tags.length ?
'No tags found in ' + that._source :
'Available tags: ' + tags.join(', ');
err.details += '\n';
err.details += !branches.length ?
'No branches found in ' + that._source :
'Available branches: ' + branches.join(', ');
throw err;
});
};
GitResolver.prototype._cleanup = function () {
var gitFolder = path.join(this._tempDir, '.git');
// Remove the .git folder
// Note that on windows, we need to chmod to 0777 before due to a bug in git
// See: https://github.com/isaacs/rimraf/issues/19
if (process.platform === 'win32') {
return Q.nfcall(chmodr, gitFolder, 0777)
.then(function () {
return Q.nfcall(rimraf, gitFolder);
}, function (err) {
// If .git does not exist, chmodr returns ENOENT
// so, we ignore that error code
if (err.code !== 'ENOENT') {
throw err;
}
});
} else {
return Q.nfcall(rimraf, gitFolder);
}
};
GitResolver.prototype._savePkgMeta = function (meta) {
var version;
if (this._resolution.type === 'version') {
version = semver.clean(this._resolution.tag);
// Warn if the package meta version is different than the resolved one
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
resolution: this._resolution,
pkgMeta: meta
});
}
// Ensure package meta version is the same as the resolution
meta.version = version;
} else {
// If resolved to a target that is not a version,
// remove the version from the meta
delete meta.version;
}
// Save version/tag/commit in the release
// Note that we can't store branches because _release is supposed to be
// an unique id of this ref.
meta._release = version ||
this._resolution.tag ||
this._resolution.commit.substr(0, 10);
// Save resolution to be used in hasNew later
meta._resolution = this._resolution;
return Resolver.prototype._savePkgMeta.call(this, meta);
};
// ------------------------------
GitResolver.versions = function (source, extra) {
var value = this._cache.versions.get(source);
if (value) {
return Q.resolve(value)
.then(function () {
var versions = this._cache.versions.get(source);
// If no extra information was requested,
// resolve simply with the versions
if (!extra) {
versions = versions.map(function (version) {
return version.version;
});
}
return versions;
}.bind(this));
}
value = this.tags(source)
.then(function (tags) {
var tag;
var version;
var versions = [];
// For each tag
for (tag in tags) {
version = semver.clean(tag);
if (version) {
versions.push({ version: version, tag: tag, commit: tags[tag] });
}
}
// Sort them by DESC order
versions.sort(function (a, b) {
return semver.rcompare(a.version, b.version);
});
this._cache.versions.set(source, versions);
// Call the function again to keep it DRY
return this.versions(source, extra);
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.versions.set(source, value);
return value;
};
GitResolver.tags = function (source) {
var value = this._cache.tags.get(source);
if (value) {
return Q.resolve(value);
}
value = this.refs(source)
.then(function (refs) {
var tags = {};
// For each line in the refs, match only the tags
refs.forEach(function (line) {
var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/);
if (match && !mout.string.endsWith(match[2], '^{}')) {
tags[match[2]] = match[1];
}
});
this._cache.tags.set(source, tags);
return tags;
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.tags.set(source, value);
return value;
};
GitResolver.branches = function (source) {
var value = this._cache.branches.get(source);
if (value) {
return Q.resolve(value);
}
value = this.refs(source)
.then(function (refs) {
var branches = {};
// For each line in the refs, extract only the heads
// Organize them in an object where keys are branches and values
// the commit hashes
refs.forEach(function (line) {
var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/);
if (match) {
branches[match[2]] = match[1];
}
});
this._cache.branches.set(source, branches);
return branches;
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.branches.set(source, value);
return value;
};
GitResolver.clearRuntimeCache = function () {
// Reset cache for branches, tags, etc
mout.object.forOwn(GitResolver._cache, function (lru) {
lru.reset();
});
};
GitResolver._cache = {
branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
refs: new LRU({ max: 50, maxAge: 5 * 60 * 1000 })
};
module.exports = GitResolver;
-256
View File
@@ -1,256 +0,0 @@
var fs = require('graceful-fs');
var path = require('path');
var Q = require('q');
var tmp = require('tmp');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var readJson = require('../../util/readJson');
var createError = require('../../util/createError');
var removeIgnores = require('../../util/removeIgnores');
tmp.setGracefulCleanup();
function Resolver(decEndpoint, config, logger) {
this._source = decEndpoint.source;
this._target = decEndpoint.target || '*';
this._name = decEndpoint.name || path.basename(this._source);
this._config = config;
this._logger = logger;
this._guessedName = !decEndpoint.name;
}
// -----------------
Resolver.prototype.getSource = function () {
return this._source;
};
Resolver.prototype.getName = function () {
return this._name;
};
Resolver.prototype.getTarget = function () {
return this._target;
};
Resolver.prototype.getTempDir = function () {
return this._tempDir;
};
Resolver.prototype.getPkgMeta = function () {
return this._pkgMeta;
};
Resolver.prototype.hasNew = function (canonicalDir, pkgMeta) {
var promise;
var metaFile;
var that = this;
// If already working, error out
if (this._working) {
return Q.reject(createError('Already working', 'EWORKING'));
}
this._working = true;
// Avoid reading the package meta if already given
if (pkgMeta) {
promise = this._hasNew(canonicalDir, pkgMeta);
// Otherwise call _hasNew with both the package meta and the canonical dir
} else {
metaFile = path.join(canonicalDir, '.bower.json');
promise = readJson(metaFile)
.spread(function (pkgMeta) {
return that._hasNew(canonicalDir, pkgMeta);
}, function (err) {
that._logger.debug('read-json', 'Failed to read ' + metaFile, {
filename: metaFile,
error: err
});
return true; // Simply resolve to true if there was an error reading the file
});
}
return promise.fin(function () {
that._working = false;
});
};
Resolver.prototype.resolve = function () {
var that = this;
// If already working, error out
if (this._working) {
return Q.reject(createError('Already working', 'EWORKING'));
}
this._working = true;
// Create temporary dir
return this._createTempDir()
// Resolve self
.then(this._resolve.bind(this))
// Read json, generating the package meta
.then(this._readJson.bind(this, null))
// Apply and save package meta
.then(function (meta) {
return that._applyPkgMeta(meta)
.then(that._savePkgMeta.bind(that, meta));
})
.then(function () {
// Resolve with the folder
return that._tempDir;
}, function (err) {
// If something went wrong, unset the temporary dir
that._tempDir = null;
throw err;
})
.fin(function () {
that._working = false;
});
};
Resolver.prototype.isCacheable = function () {
// Bypass cache for local dependencies
if (this._source &&
/^(?:file:[\/\\]{2}|[A-Z]:)?\.?\.?[\/\\]/.test(this._source)
) {
return false;
}
// We don't want to cache moving targets like branches
if (this._pkgMeta &&
this._pkgMeta._resolution &&
this._pkgMeta._resolution.type === 'branch')
{
return false;
}
return true;
};
// -----------------
// Abstract functions that must be implemented by concrete resolvers
Resolver.prototype._resolve = function () {
throw new Error('_resolve not implemented');
};
// Abstract functions that can be re-implemented by concrete resolvers
// as necessary
Resolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
return Q.resolve(true);
};
Resolver.isTargetable = function () {
return true;
};
Resolver.versions = function (source) {
return Q.resolve([]);
};
Resolver.clearRuntimeCache = function () {};
// -----------------
Resolver.prototype._createTempDir = function () {
return Q.nfcall(mkdirp, this._config.tmp)
.then(function () {
return Q.nfcall(tmp.dir, {
template: path.join(this._config.tmp, this._name + '-' + process.pid + '-XXXXXX'),
mode: 0777 & ~process.umask(),
unsafeCleanup: true
});
}.bind(this))
.then(function (dir) {
// nfcall may return multiple callback arguments as an array
return this._tempDir = Array.isArray(dir) ? dir[0] : dir;
}.bind(this));
};
Resolver.prototype._cleanTempDir = function () {
var tempDir = this._tempDir;
if (!tempDir) {
return Q.resolve();
}
// Delete and create folder
return Q.nfcall(rimraf, tempDir)
.then(function () {
return Q.nfcall(mkdirp, tempDir, 0777 & ~process.umask());
})
.then(function () {
return tempDir;
});
};
Resolver.prototype._readJson = function (dir) {
var that = this;
dir = dir || this._tempDir;
return readJson(dir, {
assume: { name: this._name }
})
.spread(function (json, deprecated) {
if (deprecated) {
that._logger.warn('deprecated', 'Package ' + that._name + ' is using the deprecated ' + deprecated);
}
return json;
});
};
Resolver.prototype._applyPkgMeta = function (meta) {
// Check if name defined in the json is different
// If so and if the name was "guessed", assume the json name
if (meta.name !== this._name && this._guessedName) {
this._name = meta.name;
}
// Handle ignore property, deleting all files from the temporary directory
// If no ignores were specified, simply resolve
if (!meta.ignore || !meta.ignore.length) {
return Q.resolve(meta);
}
// Otherwise remove them from the temp dir
return removeIgnores(this._tempDir, meta)
.then(function () {
return meta;
});
};
Resolver.prototype._savePkgMeta = function (meta) {
var that = this;
var contents;
// Store original source & target
meta._source = this._source;
meta._target = this._target;
['main', 'ignore'].forEach(function (attr) {
if (meta[attr]) return;
that._logger.log(
'warn', 'invalid-meta',
(meta.name || 'component') + ' is missing "' + attr + '" entry in bower.json'
);
});
// Stringify contents
contents = JSON.stringify(meta, null, 2);
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
.then(function () {
return that._pkgMeta = meta;
});
};
module.exports = Resolver;
@@ -1,403 +0,0 @@
var util = require('util');
var Q = require('q');
var which = require('which');
var LRU = require('lru-cache');
var mout = require('mout');
var Resolver = require('./Resolver');
var semver = require('../../util/semver');
var createError = require('../../util/createError');
var cmd = require('../../util/cmd');
var hasSvn;
// Check if svn is installed
try {
which.sync('svn');
hasSvn = true;
} catch (ex) {
hasSvn = false;
}
function SvnResolver(decEndpoint, config, logger) {
Resolver.call(this, decEndpoint, config, logger);
if (!hasSvn) {
throw createError('svn is not installed or not in the PATH', 'ENOSVN');
}
}
util.inherits(SvnResolver, Resolver);
mout.object.mixIn(SvnResolver, Resolver);
// -----------------
SvnResolver.getSource = function (source) {
var uri = this._source || source;
return uri
.replace(/^svn\+(https?|file):\/\//i, '$1://') // Change svn+http or svn+https or svn+file to http(s), file respectively
.replace('svn://', 'http://') // Change svn to http
.replace(/\/+$/, ''); // Remove trailing slashes
};
SvnResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
var oldResolution = pkgMeta._resolution || {};
return this._findResolution()
.then(function (resolution) {
// Check if resolution types are different
if (oldResolution.type !== resolution.type) {
return true;
}
// If resolved to a version, there is new content if the tags are not equal
if (resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag)) {
return true;
}
// As last check, we compare both commit hashes
return resolution.commit !== oldResolution.commit;
});
};
SvnResolver.prototype._resolve = function () {
var that = this;
return this._findResolution()
.then(function () {
return that._export();
});
};
// -----------------
SvnResolver.prototype._export = function () {
var promise;
var timer;
var reporter;
var that = this;
var resolution = this._resolution;
this.source = SvnResolver.getSource(this._source);
this._logger.action('export', resolution.tag || resolution.branch || resolution.commit, {
resolution: resolution,
to: this._tempDir
});
if (resolution.type === 'commit') {
promise = cmd('svn', ['export', '--force', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
promise = cmd('svn', ['export', '--force', this._source + '/trunk', this._tempDir]);
} else if (resolution.type === 'branch') {
promise = cmd('svn', ['export', '--force', this._source + '/branches/' + resolution.branch, this._tempDir]);
} else {
promise = cmd('svn', ['export', '--force', this._source + '/tags/' + resolution.tag, this._tempDir]);
}
// Throttle the progress reporter to 1 time each sec
reporter = mout.fn.throttle(function (data) {
var lines;
lines = data.split(/[\r\n]+/);
lines.forEach(function (line) {
if (/\d{1,3}\%/.test(line)) {
// TODO: There are some strange chars that appear once in a while (\u001b[K)
// Trim also those?
that._logger.info('progress', line.trim());
}
});
}, 1000);
// Start reporting progress after a few seconds
timer = setTimeout(function () {
promise.progress(reporter);
}, 8000);
return promise
// Add additional proxy information to the error if necessary
.fail(function (err) {
throw err;
})
// Clear timer at the end
.fin(function () {
clearTimeout(timer);
reporter.cancel();
});
};
// -----------------
SvnResolver.prototype._findResolution = function (target) {
var err;
var self = this.constructor;
var that = this;
target = target || this._target || '*';
this._source = SvnResolver.getSource(this._source);
// Target is a revision, so it's a stale target (not a moving target)
// There's nothing to do in this case
if ((/^r\d+/).test(target)) {
target = target.split('r');
this._resolution = { type: 'commit', commit: target[1] };
return Q.resolve(this._resolution);
}
// Target is a range/version
if (semver.validRange(target)) {
return self.versions(this._source, true)
.then(function (versions) {
var versionsArr,
version,
index;
versionsArr = versions.map(function (obj) { return obj.version; });
// If there are no tags and target is *,
// fallback to the latest commit on trunk
if (!versions.length && target === '*') {
return that._findResolution('trunk');
}
versionsArr = versions.map(function (obj) { return obj.version; });
// Find a satisfying version, enabling strict match so that pre-releases
// have lower priority over normal ones when target is *
index = semver.maxSatisfyingIndex(versionsArr, target, true);
if (index !== -1) {
version = versions[index];
return that._resolution = { type: 'version', tag: version.tag, commit: version.commit };
}
// Check if there's an exact branch/tag with this name as last resort
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
details: !versions.length ?
'No versions found in ' + that._source :
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
});
});
});
}
// Otherwise, target is either a tag or a branch
return Q.all([
self.branches(that._source),
self.tags(that._source)
])
.spread(function (branches, tags) {
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
if (mout.object.hasOwn(tags, target)) {
return that._resolution = { type: 'tag', tag: target, commit: tags[target] };
}
if (mout.object.hasOwn(branches, target)) {
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
}
branches = Object.keys(branches);
tags = Object.keys(tags);
err = createError('target ' + target + ' does not exist', 'ENORESTARGET');
err.details = !tags.length ?
'No tags found in ' + that._source :
'Available tags: ' + tags.join(', ');
err.details += '\n';
err.details += !branches.length ?
'No branches found in ' + that._source :
'Available branches: ' + branches.join(', ');
throw err;
});
};
SvnResolver.prototype._savePkgMeta = function (meta) {
var version;
if (this._resolution.type === 'version') {
version = semver.clean(this._resolution.tag);
// Warn if the package meta version is different than the resolved one
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
resolution: this._resolution,
pkgMeta: meta
});
}
// Ensure package meta version is the same as the resolution
meta.version = version;
} else {
// If resolved to a target that is not a version,
// remove the version from the meta
delete meta.version;
}
// Save version/tag/commit in the release
// Note that we can't store branches because _release is supposed to be
// an unique id of this ref.
meta._release = version ||
this._resolution.tag ||
this._resolution.commit;
// Save resolution to be used in hasNew later
meta._resolution = this._resolution;
return Resolver.prototype._savePkgMeta.call(this, meta);
};
// ------------------------------
SvnResolver.versions = function (source, extra) {
source = SvnResolver.getSource(source);
var value = this._cache.versions.get(source);
if (value) {
return Q.resolve(value)
.then(function () {
var versions = this._cache.versions.get(source);
// If no extra information was requested,
// resolve simply with the versions
if (!extra) {
versions = versions.map(function (version) {
return version.version;
});
}
return versions;
}.bind(this));
}
value = this.tags(source)
.then(function (tags) {
var tag;
var version;
var versions = [];
// For each tag
for (tag in tags) {
version = semver.clean(tag);
if (version) {
versions.push({ version: version, tag: tag, commit: tags[tag] });
}
}
// Sort them by DESC order
versions.sort(function (a, b) {
return semver.rcompare(a.version, b.version);
});
this._cache.versions.set(source, versions);
// Call the function again to keep it DRY
return this.versions(source, extra);
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.versions.set(source, value);
return value;
};
SvnResolver.tags = function (source) {
source = SvnResolver.getSource(source);
var value = this._cache.tags.get(source);
if (value) {
return Q.resolve(value);
}
value = cmd('svn', ['list', source + '/tags', '--verbose'])
.spread(function (stout) {
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
this._cache.tags.set(source, tags);
return tags;
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.tags.set(source, value);
return value;
};
SvnResolver.branches = function (source) {
source = SvnResolver.getSource(source);
var value = this._cache.branches.get(source);
if (value) {
return Q.resolve(value);
}
value = cmd('svn', ['list', source + '/branches', '--verbose'])
.spread(function (stout) {
var branches = SvnResolver.parseSubversionListOutput(stout.toString());
// trunk is a branch!
branches.trunk = '*';
this._cache.branches.set(source, branches);
return branches;
}.bind(this));
// Store the promise to be reused until it resolves
// to a specific value
this._cache.branches.set(source, value);
return value;
};
SvnResolver.parseSubversionListOutput = function (stout) {
var entries = {};
var lines = stout
.trim()
.split(/[\r\n]+/);
// For each line in the refs, match only the branches
lines.forEach(function (line) {
var match = line.match(/\s+([0-9]+)\s.+\s([\w.$-]+)\//i);
if (match && match[2] !== '.') {
entries[match[2]] = match[1];
}
});
return entries;
};
SvnResolver.clearRuntimeCache = function () {
// Reset cache for branches, tags, etc
mout.object.forOwn(SvnResolver._cache, function (lru) {
lru.reset();
});
};
SvnResolver._cache = {
branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 })
};
module.exports = SvnResolver;
@@ -1,284 +0,0 @@
var util = require('util');
var path = require('path');
var fs = require('graceful-fs');
var url = require('url');
var request = require('request');
var Q = require('q');
var mout = require('mout');
var junk = require('junk');
var Resolver = require('./Resolver');
var download = require('../../util/download');
var extract = require('../../util/extract');
var createError = require('../../util/createError');
function UrlResolver(decEndpoint, config, logger) {
Resolver.call(this, decEndpoint, config, logger);
// If target was specified, error out
if (this._target !== '*') {
throw createError('URL sources can\'t resolve targets', 'ENORESTARGET');
}
// If the name was guessed
if (this._guessedName) {
// Remove the ?xxx part
this._name = this._name.replace(/\?.*$/, '');
// Remove extension
this._name = this._name.substr(0, this._name.length - path.extname(this._name).length);
}
this._remote = url.parse(this._source);
}
util.inherits(UrlResolver, Resolver);
mout.object.mixIn(UrlResolver, Resolver);
// -----------------
UrlResolver.isTargetable = function () {
return false;
};
UrlResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
var oldCacheHeaders = pkgMeta._cacheHeaders || {};
var reqHeaders = {};
// If the previous cache headers contain an ETag,
// send the "If-None-Match" header with it
if (oldCacheHeaders.ETag) {
reqHeaders['If-None-Match'] = oldCacheHeaders.ETag;
}
if (this._config.userAgent) {
reqHeaders['User-Agent'] = this._config.userAgent;
}
// Make an HEAD request to the source
return Q.nfcall(request.head, this._source, {
proxy: this._remote.protocol === 'https:' ? this._config.httpsProxy : this._config.proxy,
strictSSL: this._config.strictSsl,
timeout: this._config.timeout,
headers: reqHeaders
})
// Compare new headers with the old ones
.spread(function (response) {
var cacheHeaders;
// If the server responded with 303 then the resource
// still has the same ETag
if (response.statusCode === 304) {
return false;
}
// If status code is not in the 2xx range,
// then just resolve to true
if (response.statusCode < 200 || response.statusCode >= 300) {
return true;
}
// Fallback to comparing cache headers
cacheHeaders = this._collectCacheHeaders(response);
return !mout.object.equals(oldCacheHeaders, cacheHeaders);
}.bind(this), function () {
// Assume new contents if the request failed
// Note that we do not retry the request using the "request-replay" module
// because it would take too long
return true;
});
};
// TODO: There's room for improvement by using streams if the URL
// is an archive file, by piping read stream to the zip extractor
// This will likely increase the complexity of code but might worth it
UrlResolver.prototype._resolve = function () {
// Download
return this._download()
// Parse headers
.spread(this._parseHeaders.bind(this))
// Extract file
.spread(this._extract.bind(this))
// Rename file to index
.then(this._rename.bind(this));
};
// -----------------
UrlResolver.prototype._download = function () {
var fileName = url.parse(path.basename(this._source)).pathname;
var file = path.join(this._tempDir, fileName);
var reqHeaders = {};
var that = this;
if (this._config.userAgent) {
reqHeaders['User-Agent'] = this._config.userAgent;
}
this._logger.action('download', that._source, {
url: that._source,
to: file
});
// Download the file
return download(this._source, file, {
proxy: this._remote.protocol === 'https:' ? this._config.httpsProxy : this._config.proxy,
strictSSL: this._config.strictSsl,
timeout: this._config.timeout,
headers: reqHeaders
})
.progress(function (state) {
var msg;
// Retry?
if (state.retry) {
msg = 'Download of ' + that._source + ' failed' + (state.error.code ? ' with ' + state.error.code : '') + ', ';
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
that._logger.debug('error', state.error.message, { error: state.error });
return that._logger.warn('retry', msg);
}
// Progress
msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
if (state.total) {
msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
msg += state.percent + '%';
}
that._logger.info('progress', msg);
})
.then(function (response) {
that._response = response;
return [file, response];
});
};
UrlResolver.prototype._parseHeaders = function (file, response) {
var disposition;
var newFile;
var match;
// Check if we got a Content-Disposition header
disposition = response.headers['content-disposition'];
if (!disposition) {
return Q.resolve([file, response]);
}
// Since there's various security issues with parsing this header, we only
// interpret word chars plus dots, dashes and spaces
match = disposition.match(/filename=(?:"([\w\-\. ]+)")/i);
if (!match) {
// The spec defines that the filename must be in quotes,
// though a wide range of servers do not follow the rule
match = disposition.match(/filename=([\w\-\.]+)/i);
if (!match) {
return Q.resolve([file, response]);
}
}
// Trim spaces
newFile = match[1].trim();
// The filename can't end with a dot because this is known
// to cause issues in Windows
// See: http://superuser.com/questions/230385/dots-at-end-of-file-name
if (mout.string.endsWith(newFile, '.')) {
return Q.resolve([file, response]);
}
newFile = path.join(this._tempDir, newFile);
return Q.nfcall(fs.rename, file, newFile)
.then(function () {
return [newFile, response];
});
};
UrlResolver.prototype._extract = function (file, response) {
var mimeType = response.headers['content-type'];
if (mimeType) {
// Clean everything after ; and trim the end result
mimeType = mimeType.split(';')[0].trim();
// Some servers add quotes around the content-type, so we trim that also
mimeType = mout.string.trim(mimeType, ['"', '\'']);
}
if (!extract.canExtract(file, mimeType)) {
return Q.resolve();
}
this._logger.action('extract', path.basename(this._source), {
archive: file,
to: this._tempDir
});
return extract(file, this._tempDir, {
mimeType: mimeType
});
};
UrlResolver.prototype._rename = function () {
return Q.nfcall(fs.readdir, this._tempDir)
.then(function (files) {
var file;
var oldPath;
var newPath;
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
// Only rename if there's only one file and it's not the json
if (files.length === 1 && !/^(component|bower)\.json$/.test(files[0])) {
file = files[0];
this._singleFile = 'index' + path.extname(file);
oldPath = path.join(this._tempDir, file);
newPath = path.join(this._tempDir, this._singleFile);
return Q.nfcall(fs.rename, oldPath, newPath);
}
}.bind(this));
};
UrlResolver.prototype._savePkgMeta = function (meta) {
// Store collected headers in the package meta
meta._cacheHeaders = this._collectCacheHeaders(this._response);
// Store ETAG under _release
if (meta._cacheHeaders.ETag) {
meta._release = 'e-tag:' + mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
}
// Store main if is a single file
if (this._singleFile) {
meta.main = this._singleFile;
}
return Resolver.prototype._savePkgMeta.call(this, meta);
};
UrlResolver.prototype._collectCacheHeaders = function (res) {
var headers = {};
// Collect cache headers
this.constructor._cacheHeaders.forEach(function (name) {
var value = res.headers[name.toLowerCase()];
if (value != null) {
headers[name] = value;
}
});
return headers;
};
UrlResolver._cacheHeaders = [
'Content-MD5',
'ETag',
'Last-Modified',
'Content-Language',
'Content-Length',
'Content-Type',
'Content-Disposition'
];
module.exports = UrlResolver;
-8
View File
@@ -1,8 +0,0 @@
module.exports = {
GitFs: require('./GitFsResolver'),
GitRemote: require('./GitRemoteResolver'),
GitHub: require('./GitHubResolver'),
Svn: require('./SvnResolver'),
Fs: require('./FsResolver'),
Url: require('./UrlResolver')
};
-96
View File
@@ -1,96 +0,0 @@
var mout = require('mout');
var cmd = require('../util/cmd');
var Q = require('q');
var shellquote = require('shell-quote');
var orderByDependencies = function (packages, installed, json) {
var ordered = [];
installed = mout.object.keys(installed);
var depsSatisfied = function (packageName) {
return mout.array.difference(mout.object.keys(packages[packageName].dependencies), installed, ordered).length === 0;
};
var depsFromBowerJson = json && json.dependencies ? mout.object.keys(json.dependencies) : [];
var packageNames = mout.object.keys(packages);
//get the list of the packages that are specified in bower.json in that order
//its nice to maintain that order for users
var desiredOrder = mout.array.intersection(depsFromBowerJson, packageNames);
//then add to the end any remaining packages that werent in bower.json
desiredOrder = desiredOrder.concat(mout.array.difference(packageNames, desiredOrder));
//the desired order isn't necessarily a correct dependency specific order
//so we ensure that below
var resolvedOne = true;
while (resolvedOne) {
resolvedOne = false;
for (var i = 0; i < desiredOrder.length; i++) {
var packageName = desiredOrder[i];
if (depsSatisfied(packageName)) {
ordered.push(packageName);
mout.array.remove(desiredOrder, packageName);
//as soon as we resolve a package start the loop again
resolvedOne = true;
break;
}
}
if (!resolvedOne && desiredOrder.length > 0) {
//if we're here then some package(s) doesn't have all its deps satisified
//so lets just jam those names on the end
ordered = ordered.concat(desiredOrder);
}
}
return ordered;
};
var run = function (cmdString, action, logger, config) {
logger.action(action, cmdString);
//pass env + BOWER_PID so callees can identify a preinstall+postinstall from the same bower instance
var env = mout.object.mixIn({ 'BOWER_PID': process.pid }, process.env);
var args = shellquote.parse(cmdString, env);
var cmdName = args[0];
mout.array.remove(args, cmdName); //no rest() in mout
var options = {
cwd: config.cwd,
env: env
};
var promise = cmd(cmdName, args, options);
promise.progress(function (progress) {
progress.split('\n').forEach(function (line) {
if (line) {
logger.action(action, line);
}
});
});
return promise;
};
var hook = function (action, ordered, config, logger, packages, installed, json) {
if (mout.object.keys(packages).length === 0 || !config.scripts || !config.scripts[action]) {
/*jshint newcap: false */
return Q();
}
var orderedPackages = ordered ? orderByDependencies(packages, installed, json) : mout.object.keys(packages);
var cmdString = mout.string.replace(config.scripts[action], '%', orderedPackages.join(' '));
return run(cmdString, action, logger, config);
};
module.exports = {
preuninstall: mout.function.partial(hook, 'preuninstall', false),
preinstall: mout.function.partial(hook, 'preinstall', true),
postinstall: mout.function.partial(hook, 'postinstall', true),
//only exposed for test
_orderByDependencies: orderByDependencies
};
-43
View File
@@ -1,43 +0,0 @@
var abbrev = require('abbrev');
var mout = require('mout');
var commands = require('./commands');
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) {
prefix = prefix || '';
stack = stack || [];
mout.object.forOwn(obj, function (value, name) {
name = prefix + name;
stack.push(name);
if (typeof value === 'object' && !value.line) {
expandNames(value, name + ' ', stack);
}
});
return stack;
}
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')(),
abbreviations: abbreviations,
reset: clearRuntimeCache
};
-135
View File
@@ -1,135 +0,0 @@
var chalk = require('chalk');
var Q = require('q');
var promptly = require('promptly');
var createError = require('../util/createError');
function JsonRenderer() {
this._nrLogs = 0;
}
JsonRenderer.prototype.end = function (data) {
if (this._nrLogs) {
process.stderr.write(']\n');
}
if (data) {
process.stdout.write(this._stringify(data) + '\n');
}
};
JsonRenderer.prototype.error = function (err) {
var message = err.message;
var stack;
err.id = err.code || 'error';
err.level = 'error';
err.data = err.data || {};
// Need to set message again because it is
// not enumerable in some cases
delete err.message;
err.message = message;
// Stack
/*jshint camelcase:false*/
stack = err.fstream_stack || err.stack || 'N/A';
err.stacktrace = (Array.isArray(stack) ? stack.join('\n') : stack);
/*jshint camelcase:true*/
this.log(err);
this.end();
};
JsonRenderer.prototype.log = function (log) {
if (!this._nrLogs) {
process.stderr.write('[');
} else {
process.stderr.write(', ');
}
process.stderr.write(this._stringify(log));
this._nrLogs++;
};
JsonRenderer.prototype.prompt = function (prompts) {
var promise = Q.resolve();
var answers = {};
var that = this;
prompts.forEach(function (prompt) {
var opts;
var funcName;
// Strip colors
prompt.message = chalk.stripColor(prompt.message);
// Prompt
opts = {
silent: true, // To not mess with JSON output
trim: false, // To allow " " to not assume the default value
default: prompt.default == null ? '' : prompt.default, // If default is null, make it '' so that it does not retry
validator: !prompt.validate ? null : function (value) {
var ret = prompt.validate(value);
if (typeof ret === 'string') {
throw ret;
}
return value;
},
};
// For now only "input", "confirm" and "password" are supported
switch (prompt.type) {
case 'input':
funcName = 'prompt';
break;
case 'confirm':
case 'password':
funcName = prompt.type;
break;
case 'checkbox':
funcName = 'prompt';
break;
default:
promise = promise.then(function () {
throw createError('Unknown prompt type', 'ENOTSUP');
});
return;
}
promise = promise.then(function () {
// Log
prompt.level = 'prompt';
that.log(prompt);
return Q.nfcall(promptly[funcName], '', opts)
.then(function (answer) {
answers[prompt.name] = answer;
});
});
if (prompt.type === 'checkbox') {
promise = promise.then(function () {
answers[prompt.name] = answers[prompt.name].split(',');
});
}
});
return promise.then(function () {
return answers;
});
};
// -------------------------
JsonRenderer.prototype._stringify = function (log) {
// To json
var str = JSON.stringify(log, null, ' ');
// Remove colors in case some log has colors..
str = chalk.stripColor(str);
return str;
};
module.exports = JsonRenderer;
@@ -1,495 +0,0 @@
var chalk = require('chalk');
var path = require('path');
var mout = require('mout');
var archy = require('archy');
var Q = require('q');
var stringifyObject = require('stringify-object');
var os = require('os');
var pkg = require(path.join(__dirname, '../..', 'package.json'));
var template = require('../util/template');
function StandardRenderer(command, config) {
this._sizes = {
id: 13, // Id max chars
label: 20, // Label max chars
sumup: 5 // Amount to sum when the label exceeds
};
this._colors = {
warn: chalk.yellow,
error: chalk.red,
conflict: chalk.magenta,
debug: chalk.gray,
default: chalk.cyan
};
this._command = command;
this._config = config;
if (this.constructor._wideCommands.indexOf(command) === -1) {
this._compact = true;
} else {
this._compact = process.stdout.columns < 120;
}
var exitOnPipeError = function (err) {
if (err.code === 'EPIPE') {
process.exit(0);
}
};
// It happens when piping command to "head" util
process.stdout.on('error', exitOnPipeError);
process.stderr.on('error', exitOnPipeError);
}
StandardRenderer.prototype.end = function (data) {
var method = '_' + mout.string.camelCase(this._command);
if (this[method]) {
this[method](data);
}
};
StandardRenderer.prototype.error = function (err) {
var str;
var stack;
this._guessOrigin(err);
err.id = err.code || 'error';
err.level = 'error';
str = this._prefix(err) + ' ' + err.message.replace(/\r?\n/g, ' ').trim() + '\n';
this._write(process.stderr, 'bower ' + str);
// Check if additional details were provided
if (err.details) {
str = chalk.yellow('\nAdditional error details:\n') + err.details.trim() + '\n';
this._write(process.stderr, str);
}
// Print trace if verbose, the error has no code
// or if the error is a node error
if (this._config.verbose || !err.code || err.errno) {
/*jshint camelcase:false*/
stack = err.fstream_stack || err.stack || 'N/A';
str = chalk.yellow('\nStack trace:\n');
str += (Array.isArray(stack) ? stack.join('\n') : stack) + '\n';
str += chalk.yellow('\nConsole trace:\n');
/*jshint camelcase:true*/
this._write(process.stderr, str);
console.trace();
// Print bower version, node version and system info.
this._write(process.stderr, chalk.yellow('\nSystem info:\n'));
this._write(process.stderr, 'Bower version: ' + pkg.version + '\n');
this._write(process.stderr, 'Node version: ' + process.versions.node + '\n');
this._write(process.stderr, 'OS: ' + os.type() + ' ' + os.release() + ' ' + os.arch() + '\n');
}
};
StandardRenderer.prototype.log = function (log) {
var method = '_' + mout.string.camelCase(log.id) + 'Log';
this._guessOrigin(log);
// Call render method for this log entry or the generic one
if (this[method]) {
this[method](log);
} else {
this._genericLog(log);
}
};
StandardRenderer.prototype.prompt = function (prompts) {
var deferred;
// Strip colors from the prompt if color is disabled
if (!this._config.color) {
prompts.forEach(function (prompt) {
prompt.message = chalk.stripColor(prompt.message);
});
}
// Prompt
deferred = Q.defer();
var inquirer = require('inquirer');
inquirer.prompt(prompts, deferred.resolve);
return deferred.promise;
};
// -------------------------
StandardRenderer.prototype._help = function (data) {
var str;
var that = this;
var specific;
if (!data.command) {
str = template.render('std/help.std', data);
that._write(process.stdout, str);
} else {
// Check if a specific template exists for the command
specific = 'std/help-' + data.command.replace(/\s+/g, '/') + '.std';
if (template.exists(specific)) {
str = template.render(specific, data);
} else {
str = template.render('std/help-generic.std', data);
}
that._write(process.stdout, str);
}
};
StandardRenderer.prototype._install = function (packages) {
var str = '';
mout.object.forOwn(packages, function (pkg) {
var cliTree;
// List only 1 level deep dependencies
mout.object.forOwn(pkg.dependencies, function (dependency) {
dependency.dependencies = {};
});
// Make canonical dir relative
pkg.canonicalDir = path.relative(this._config.cwd, pkg.canonicalDir);
// Signal as root
pkg.root = true;
cliTree = this._tree2archy(pkg);
str += '\n' + archy(cliTree);
}, this);
if (str) {
this._write(process.stdout, str);
}
};
StandardRenderer.prototype._update = function (packages) {
this._install(packages);
};
StandardRenderer.prototype._list = function (tree) {
var cliTree;
if (tree.pkgMeta) {
tree.root = true;
cliTree = archy(this._tree2archy(tree));
} else {
cliTree = stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
}
this._write(process.stdout, cliTree);
};
StandardRenderer.prototype._search = function (results) {
var str = template.render('std/search-results.std', results);
this._write(process.stdout, str);
};
StandardRenderer.prototype._info = function (data) {
var str = '';
var pkgMeta = data;
var includeVersions = false;
// If the response is the whole package info, the package meta
// is under the "latest" property
if (typeof data === 'object' && data.versions) {
pkgMeta = data.latest;
includeVersions = true;
}
// Render package meta
if (pkgMeta != null) {
str += '\n' + this._highlightJson(pkgMeta) + '\n';
}
// Render the versions at the end
if (includeVersions) {
str += '\n' + template.render('std/info.std', data);
}
this._write(process.stdout, str);
};
StandardRenderer.prototype._lookup = function (data) {
var str = template.render('std/lookup.std', data);
this._write(process.stdout, str);
};
StandardRenderer.prototype._link = function (data) {
this._sizes.id = 4;
this.log({
id: 'link',
level: 'info',
message: data.dst + ' > ' + data.src
});
// Print also a tree of the installed packages
if (data.installed) {
this._install(data.installed);
}
};
StandardRenderer.prototype._register = function (data) {
var str;
// If no data passed, it means the user aborted
if (!data) {
return;
}
str = '\n' + template.render('std/register.std', data);
this._write(process.stdout, str);
};
StandardRenderer.prototype._cacheList = function (entries) {
entries.forEach(function (entry) {
var pkgMeta = entry.pkgMeta;
var version = pkgMeta.version || pkgMeta._target;
this._write(process.stdout, pkgMeta.name + '=' + pkgMeta._source + '#' + version + '\n');
}, this);
};
// -------------------------
StandardRenderer.prototype._genericLog = function (log) {
var stream;
var str;
if (log.level === 'warn') {
stream = process.stderr;
} else {
stream = process.stdout;
}
str = this._prefix(log) + ' ' + log.message + '\n';
this._write(stream, 'bower ' + str);
};
StandardRenderer.prototype._checkoutLog = function (log) {
if (this._compact) {
log.message = log.origin.split('#')[0] + '#' + log.message;
}
this._genericLog(log);
};
StandardRenderer.prototype._progressLog = function (log) {
if (this._compact) {
log.message = log.origin + ' ' + log.message;
}
this._genericLog(log);
};
StandardRenderer.prototype._extractLog = function (log) {
if (this._compact) {
log.message = log.origin + ' ' + log.message;
}
this._genericLog(log);
};
StandardRenderer.prototype._incompatibleLog = function (log) {
var str;
var templatePath;
// Generate dependants string for each pick
log.data.picks.forEach(function (pick) {
pick.dependants = pick.dependants.map(function (dependant) {
var release = dependant.pkgMeta._release;
return dependant.endpoint.name + (release ? '#' + release : '');
}).join(', ');
});
templatePath = log.data.suitable ? 'std/conflict-resolved.std' : 'std/conflict.std';
str = template.render(templatePath, log.data);
this._write(process.stdout, '\n');
this._write(process.stdout, str);
this._write(process.stdout, '\n');
};
StandardRenderer.prototype._solvedLog = function (log) {
this._incompatibleLog(log);
};
StandardRenderer.prototype._jsonLog = function (log) {
this._write(process.stdout, '\n' + this._highlightJson(log.data.json) + '\n\n');
};
StandardRenderer.prototype._cachedEntryLog = function (log) {
if (this._compact) {
log.message = log.origin;
}
this._genericLog(log);
};
// -------------------------
StandardRenderer.prototype._guessOrigin = function (log) {
var data = log.data;
if (!data) {
return;
}
if (data.endpoint) {
log.origin = data.endpoint.name || (data.registry && data.endpoint.source);
// Resort to using the resolver name for unnamed endpoints
if (!log.origin && data.resolver) {
log.origin = data.resolver.name;
}
if (log.origin && data.endpoint.target) {
log.origin += '#' + data.endpoint.target;
}
} else if (data.name) {
log.origin = data.name;
if (data.version) {
log.origin += '#' + data.version;
}
}
};
StandardRenderer.prototype._prefix = function (log) {
var label;
var length;
var nrSpaces;
var id = this.constructor._idMappings[log.id] || log.id;
var idColor = this._colors[log.level] || this._colors.default;
if (this._compact) {
// If there's not enough space for the id, adjust it
// for subsequent logs
if (id.length > this._sizes.id) {
this._sizes.id = id.length += this._sizes.sumup;
}
return idColor(mout.string.rpad(id, this._sizes.id));
}
// Construct the label
label = log.origin || '';
length = id.length + label.length + 1;
nrSpaces = this._sizes.id + this._sizes.label - length;
// Ensure at least one space between the label and the id
if (nrSpaces < 1) {
// Also adjust the label size for subsequent logs
this._sizes.label = label.length + this._sizes.sumup;
nrSpaces = this._sizes.id + this._sizes.label - length;
}
return chalk.green(label) + mout.string.repeat(' ', nrSpaces) + idColor(id);
};
StandardRenderer.prototype._write = function (stream, str) {
if (!this._config.color) {
str = chalk.stripColor(str);
}
stream.write(str);
};
StandardRenderer.prototype._highlightJson = function (json) {
var cardinal = require('cardinal');
return cardinal.highlight(stringifyObject(json, { indent: ' ' }), {
theme: {
String: {
_default: function (str) {
return chalk.cyan(str);
}
},
Identifier: {
_default: function (str) {
return chalk.green(str);
}
}
},
json: true
});
};
StandardRenderer.prototype._tree2archy = function (node) {
var dependencies = mout.object.values(node.dependencies);
var version = !node.missing ? node.pkgMeta._release || node.pkgMeta.version : null;
var label = node.endpoint.name + (version ? '#' + version : '');
var update;
if (node.root) {
label += ' ' + node.canonicalDir;
}
// State labels
if (node.missing) {
label += chalk.red(' not installed');
return label;
}
if (node.different) {
label += chalk.red(' different');
}
if (node.linked) {
label += chalk.magenta(' linked');
}
if (node.incompatible) {
label += chalk.yellow(' incompatible') + ' with ' + node.endpoint.target;
} else if (node.extraneous) {
label += chalk.green(' extraneous');
}
// New versions
if (node.update) {
update = '';
if (node.update.target && node.pkgMeta.version !== node.update.target) {
update += node.update.target + ' available';
}
if (node.update.latest !== node.update.target) {
update += (update ? ', ' : '');
update += 'latest is ' + node.update.latest;
}
if (update) {
label += ' (' + chalk.cyan(update) + ')';
}
}
if (!dependencies.length) {
return label;
}
return {
label: label,
nodes: mout.object.values(dependencies).map(this._tree2archy, this)
};
};
StandardRenderer._wideCommands = [
'install',
'update',
'link',
'info',
'home',
'register'
];
StandardRenderer._idMappings = {
'mutual': 'conflict',
'cached-entry': 'cached'
};
module.exports = StandardRenderer;
-4
View File
@@ -1,4 +0,0 @@
module.exports = {
Json: require('./JsonRenderer'),
Standard: require('./StandardRenderer')
};
-88
View File
@@ -1,88 +0,0 @@
var Q = require('q');
var mout = require('mout');
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 (config) {
var deferred = Q.defer();
// 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();
}
return deferred.promise;
};
var Tracker = analytics.Tracker = function Tracker(config) {
if (!config.analytics) {
this.track = function noop() {};
}
};
Tracker.prototype.track = function track() {
ensureInsight();
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));
};
-49
View File
@@ -1,49 +0,0 @@
var mout = require('mout');
var nopt = require('nopt');
var renderers = require('../renderers');
function readOptions(options, argv) {
var types;
var noptOptions;
var parsedOptions = {};
var shorthands = {};
if (Array.isArray(options)) {
argv = options;
options = {};
} else {
options = options || {};
}
types = mout.object.map(options, function (option) {
return option.type;
});
mout.object.forOwn(options, function (option, name) {
shorthands[option.shorthand] = '--' + name;
});
noptOptions = nopt(types, shorthands, argv);
// Filter only the specified options because nopt parses every --
// Also make them camel case
mout.object.forOwn(noptOptions, function (value, key) {
if (options[key]) {
parsedOptions[mout.string.camelCase(key)] = value;
}
});
parsedOptions.argv = noptOptions.argv;
return parsedOptions;
}
function getRenderer(command, json, config) {
if (config.json || json) {
return new renderers.Json(command, config);
}
return new renderers.Standard(command, config);
}
module.exports.readOptions = readOptions;
module.exports.getRenderer = getRenderer;
-120
View File
@@ -1,120 +0,0 @@
var cp = require('child_process');
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. 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;
var isWin = process.platform === 'win32';
if (isWin) {
winBatchExtensions = ['.bat', '.cmd'];
winWhichCache = {};
}
function getWindowsCommand(command) {
var fullCommand;
var extension;
// Do we got the value converted in the cache?
if (mout.object.hasOwn(winWhichCache, command)) {
return winWhichCache[command];
}
// Use which to retrieve the full command, which puts the extension in the end
try {
fullCommand = which.sync(command);
} catch (err) {
return winWhichCache[command] = command;
}
extension = path.extname(fullCommand).toLowerCase();
// Does it need to be converted?
if (winBatchExtensions.indexOf(extension) === -1) {
return winWhichCache[command] = command;
}
return winWhichCache[command] = fullCommand;
}
// Executes a shell command, buffering the stdout and stderr
// 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 executeCmd(command, args, options) {
var process;
var stderr = '';
var stdout = '';
var deferred = Q.defer();
// Windows workaround for .bat and .cmd files, see #626
if (isWin) {
command = getWindowsCommand(command);
}
// Buffer output, reporting progress
process = cp.spawn(command, args, options);
process.stdout.on('data', function (data) {
data = data.toString();
deferred.notify(data);
stdout += data;
});
process.stderr.on('data', function (data) {
data = data.toString();
deferred.notify(data);
stderr += data;
});
// If there is an error spawning the command, reject the promise
process.on('error', function (error) {
return deferred.reject(error);
});
// Listen to the close event instead of exit
// They are similar but close ensures that streams are flushed
process.on('close', function (code) {
var fullCommand;
var error;
if (code) {
// Generate the full command to be presented in the error message
if (!Array.isArray(args)) {
args = [];
}
fullCommand = command;
fullCommand += args.length ? ' ' + args.join(' ') : '';
// Build the error instance
error = createError('Failed to execute "' + fullCommand + '", exit code of #' + code, 'ECMDERR', {
details: stderr,
exitCode: code
});
return deferred.reject(error);
}
return deferred.resolve([stdout, stderr]);
});
return deferred.promise;
}
function cmd(command, args, options) {
return throttler.enqueue(executeCmd.bind(null, command, args, options));
}
module.exports = cmd;
-114
View File
@@ -1,114 +0,0 @@
var fstream = require('fstream');
var fstreamIgnore = require('fstream-ignore');
var fs = require('graceful-fs');
var Q = require('q');
function copy(reader, writer) {
var deferred;
var ignore;
// Filter symlinks because they are not 100% portable, specially
// when linking between different drives
// Following can't be enabled either because symlinks that reference
// another symlinks will get filtered
// See: https://github.com/bower/bower/issues/699
reader.filter = filterSymlinks;
reader.follow = false;
if (reader.type === 'Directory' && reader.ignore) {
ignore = reader.ignore;
reader = fstreamIgnore(reader);
reader.addIgnoreRules(ignore);
} else {
reader = fstream.Reader(reader);
}
deferred = Q.defer();
reader
.on('error', deferred.reject)
// Pipe to writer
.pipe(fstream.Writer(writer))
.on('error', deferred.reject)
.on('close', deferred.resolve);
return deferred.promise;
}
function copyMode(src, dst) {
return Q.nfcall(fs.stat, src)
.then(function (stat) {
return Q.nfcall(fs.chmod, dst, stat.mode);
});
}
function filterSymlinks(entry) {
return entry.type !== 'SymbolicLink';
}
function parseOptions(opts) {
opts = opts || {};
if (opts.mode != null) {
opts.copyMode = false;
} else if (opts.copyMode == null) {
opts.copyMode = true;
}
return opts;
}
// ---------------------
// Available options:
// - mode: force final mode of dst (defaults to null)
// - copyMode: copy mode of src to dst, only if mode is not specified (defaults to true)
function copyFile(src, dst, opts) {
var promise;
opts = parseOptions(opts);
promise = copy({
path: src,
type: 'File'
}, {
path: dst,
mode: opts.mode,
type: 'File'
});
if (opts.copyMode) {
promise = promise.then(copyMode.bind(copyMode, src, dst));
}
return promise;
}
// Available options:
// - ignore: array of patterns to be ignored (defaults to null)
// - mode: force final mode of dst (defaults to null)
// - copyMode: copy mode of src to dst, only if mode is not specified (defaults to true)
function copyDir(src, dst, opts) {
var promise;
opts = parseOptions(opts);
promise = copy({
path: src,
type: 'Directory',
ignore: opts.ignore
}, {
path: dst,
mode: opts.mode,
type: 'Directory'
});
if (opts.copyMode) {
promise = promise.then(copyMode.bind(copyMode, src, dst));
}
return promise;
}
module.exports.copyDir = copyDir;
module.exports.copyFile = copyFile;
-14
View File
@@ -1,14 +0,0 @@
var mout = require('mout');
function createError(msg, code, props) {
var err = new Error(msg);
err.code = code;
if (props) {
mout.object.mixIn(err, props);
}
return err;
}
module.exports = createError;
-50
View File
@@ -1,50 +0,0 @@
var fs = require('graceful-fs');
var path = require('path');
var Q = require('q');
var mkdirp = require('mkdirp');
var createError = require('./createError');
var isWin = process.platform === 'win32';
function createLink(src, dst, type) {
var dstDir = path.dirname(dst);
// Create directory
return Q.nfcall(mkdirp, dstDir)
// Check if source exists
.then(function () {
return Q.nfcall(fs.stat, src)
.fail(function (error) {
if (error.code === 'ENOENT') {
throw createError('Failed to create link to ' + path.basename(src), 'ENOENT', {
details: src + ' does not exist or points to a non-existent file'
});
}
throw error;
});
})
// Create symlink
.then(function (stat) {
type = type || (stat.isDirectory() ? 'dir' : 'file');
return Q.nfcall(fs.symlink, src, dst, type)
.fail(function (err) {
if (!isWin || err.code !== 'EPERM') {
throw err;
}
// Try with type "junction" on Windows
// Junctions behave equally to true symlinks and can be created in
// non elevated terminal (well, not always..)
return Q.nfcall(fs.symlink, src, dst, 'junction')
.fail(function (err) {
throw createError('Unable to create link to ' + path.basename(src), err.code, {
details: err.message.trim() + '\n\nTry running this command in an elevated terminal (run as root/administrator).'
});
});
});
});
}
module.exports = createLink;
-109
View File
@@ -1,109 +0,0 @@
var progress = require('request-progress');
var request = require('request');
var Q = require('q');
var mout = require('mout');
var retry = require('retry');
var fs = require('graceful-fs');
var createError = require('./createError');
var errorCodes = [
'EADDRINFO',
'ETIMEDOUT',
'ECONNRESET',
'ESOCKETTIMEDOUT'
];
function download(url, file, options) {
var operation;
var response;
var deferred = Q.defer();
var progressDelay = 8000;
options = mout.object.mixIn({
retries: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 35000,
randomize: true
}, options || {});
// Retry on network errors
operation = retry.operation(options);
operation.attempt(function () {
var req;
var writeStream;
var contentLength;
var bytesDownloaded = 0;
req = progress(request(url, options), {
delay: progressDelay
})
.on('response', function (res) {
var status = res.statusCode;
if (status < 200 || status >= 300) {
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
}
response = res;
contentLength = Number(res.headers['content-length']);
})
.on('data', function (data) {
bytesDownloaded += data.length;
})
.on('progress', function (state) {
deferred.notify(state);
})
.on('end', function () {
// Check if the whole file was downloaded
// In some unstable connections the ACK/FIN packet might be sent in the
// middle of the download
// See: https://github.com/joyent/node/issues/6143
if (contentLength && bytesDownloaded < contentLength) {
req.emit('error', createError('Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read', 'EINCOMPLETE'));
}
})
.on('error', function (error) {
var timeout = operation._timeouts[0];
// Reject if error is not a network error
if (errorCodes.indexOf(error.code) === -1) {
return deferred.reject(error);
}
// Next attempt will start reporting download progress immediately
progressDelay = 0;
// Check if there are more retries
if (operation.retry(error)) {
// Ensure that there are no more events from this request
req.removeAllListeners();
req.on('error', function () {});
// Ensure that there are no more events from the write stream
writeStream.removeAllListeners();
writeStream.on('error', function () {});
return deferred.notify({
retry: true,
delay: timeout,
error: error
});
}
// No more retries, reject!
deferred.reject(error);
});
// Pipe read stream to write stream
writeStream = req
.pipe(fs.createWriteStream(file))
.on('error', deferred.reject)
.on('close', function () {
deferred.resolve(response);
});
});
return deferred.promise;
}
module.exports = download;
-228
View File
@@ -1,228 +0,0 @@
var path = require('path');
var fs = require('graceful-fs');
var zlib = require('zlib');
var DecompressZip = require('decompress-zip');
var tar = require('tar-fs');
var Q = require('q');
var mout = require('mout');
var junk = require('junk');
var createError = require('./createError');
// This forces the default chunk size to something small in an attempt
// to avoid issue #314
zlib.Z_DEFAULT_CHUNK = 1024 * 8;
var extractors;
var extractorTypes;
extractors = {
'.zip': extractZip,
'.tar': extractTar,
'.tar.gz': extractTarGz,
'.tgz': extractTarGz,
'.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
};
extractorTypes = Object.keys(extractors);
function extractZip(archive, dst) {
var deferred = Q.defer();
new DecompressZip(archive)
.on('error', deferred.reject)
.on('extract', deferred.resolve.bind(deferred, dst))
.extract({
path: dst,
follow: false, // Do not follow symlinks (#699)
filter: filterSymlinks // Filter symlink files
});
return deferred.promise;
}
function extractTar(archive, dst) {
var deferred = Q.defer();
fs.createReadStream(archive)
.on('error', deferred.reject)
.pipe(tar.extract(dst, {
ignore: isSymlink // Filter symlink files
}))
.on('error', deferred.reject)
.on('finish', deferred.resolve.bind(deferred, dst));
return deferred.promise;
}
function extractTarGz(archive, dst) {
var deferred = Q.defer();
fs.createReadStream(archive)
.on('error', deferred.reject)
.pipe(zlib.createGunzip())
.on('error', deferred.reject)
.pipe(tar.extract(dst, {
ignore: isSymlink // Filter symlink files
}))
.on('error', deferred.reject)
.on('finish', deferred.resolve.bind(deferred, dst));
return deferred.promise;
}
function extractGz(archive, dst) {
var deferred = Q.defer();
fs.createReadStream(archive)
.on('error', deferred.reject)
.pipe(zlib.createGunzip())
.on('error', deferred.reject)
.pipe(fs.createWriteStream(dst))
.on('error', deferred.reject)
.on('close', deferred.resolve.bind(deferred, dst));
return deferred.promise;
}
function isSymlink(entry) {
return entry.type === 'SymbolicLink';
}
function filterSymlinks(entry) {
return entry.type !== 'SymbolicLink';
}
function getExtractor(archive) {
// Make the archive lower case to match against the types
// This ensures that upper-cased extensions work
archive = archive.toLowerCase();
var type = mout.array.find(extractorTypes, function (type) {
return mout.string.endsWith(archive, type);
});
return type ? extractors[type] : null;
}
function isSingleDir(dir) {
return Q.nfcall(fs.readdir, dir)
.then(function (files) {
var singleDir;
// Remove any OS specific files from the files array
// before checking its length
files = files.filter(junk.isnt);
if (files.length !== 1) {
return false;
}
singleDir = path.join(dir, files[0]);
return Q.nfcall(fs.stat, singleDir)
.then(function (stat) {
return stat.isDirectory() ? singleDir : false;
});
});
}
function moveSingleDirContents(dir) {
var destDir = path.dirname(dir);
return Q.nfcall(fs.readdir, dir)
.then(function (files) {
var promises;
promises = files.map(function (file) {
var src = path.join(dir, file);
var dst = path.join(destDir, file);
return Q.nfcall(fs.rename, src, dst);
});
return Q.all(promises);
})
.then(function () {
return Q.nfcall(fs.rmdir, dir);
});
}
// -----------------------------
function canExtract(src, mimeType) {
if (mimeType && mimeType !== 'application/octet-stream') {
return !!getExtractor(mimeType);
}
return !!getExtractor(src);
}
// Available options:
// - keepArchive: true to keep the archive afterwards (defaults to false)
// - keepStructure: true to keep the extracted structure unchanged (defaults to false)
function extract(src, dst, opts) {
var extractor;
var promise;
opts = opts || {};
extractor = getExtractor(src);
// Try to get extractor from mime type
if (!extractor && opts.mimeType) {
extractor = getExtractor(opts.mimeType);
}
// If extractor is null, then the archive type is unknown
if (!extractor) {
return Q.reject(createError('File ' + src + ' is not a known archive', 'ENOTARCHIVE'));
}
// Check archive file size
promise = Q.nfcall(fs.stat, src)
.then(function (stat) {
if (stat.size <= 8) {
throw createError('File ' + src + ' is an invalid archive', 'ENOTARCHIVE');
}
// Extract archive
return extractor(src, dst);
});
// TODO: There's an issue here if the src and dst are the same and
// The zip name is the same as some of the zip file contents
// Maybe create a temp directory inside dst, unzip it there,
// unlink zip and then move contents
// Remove archive
if (!opts.keepArchive) {
promise = promise
.then(function () {
return Q.nfcall(fs.unlink, src);
});
}
// Move contents if a single directory was extracted
if (!opts.keepStructure) {
promise = promise
.then(function () {
return isSingleDir(dst);
})
.then(function (singleDir) {
return singleDir ? moveSingleDirContents(singleDir) : null;
});
}
// Resolve promise to the dst dir
return promise.then(function () {
return dst;
});
}
module.exports = extract;
module.exports.canExtract = canExtract;
-7
View File
@@ -1,7 +0,0 @@
var crypto = require('crypto');
function md5(contents) {
return crypto.createHash('md5').update(contents).digest('hex');
}
module.exports = md5;
-43
View File
@@ -1,43 +0,0 @@
var path = require('path');
var bowerJson = require('bower-json');
var Q = require('q');
// The valid options are the same as bower-json#readFile.
// If the "assume" option is passed, it will be used if no json file was found
// This promise is resolved with [json, deprecated, assumed]
// - json: The read json
// - deprecated: The deprecated filename being used or false otherwise
// - assumed: True if a dummy json was returned if no json file was found, false otherwise
function readJson(file, options) {
options = options || {};
// Read
return Q.nfcall(bowerJson.read, file, options)
.spread(function (json, jsonFile) {
var deprecated;
jsonFile = path.basename(jsonFile);
deprecated = jsonFile === 'component.json' ? jsonFile : false;
return [json, deprecated, false];
}, function (err) {
// No json file was found, assume one
if (err.code === 'ENOENT' && options.assume) {
return [bowerJson.parse(options.assume, options), false, true];
}
err.details = err.message;
if (err.file) {
err.message = 'Failed to read ' + err.file;
err.data = { filename: err.file };
} else {
err.message = 'Failed to read json from ' + file;
}
throw err;
});
}
module.exports = readJson;
-66
View File
@@ -1,66 +0,0 @@
var path = require('path');
var rimraf = require('rimraf');
var fstreamIgnore = require('fstream-ignore');
var mout = require('mout');
var Q = require('q');
function removeIgnores(dir, meta) {
var reader;
var applyIgnores;
var deferred = Q.defer();
var ignored = [];
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(meta.ignore || []);
// Monkey patch applyIgnores such that we get hold of all ignored files
applyIgnores = reader.applyIgnores;
reader.applyIgnores = function (entry) {
var ret = applyIgnores.apply(this, arguments);
if (!ret) {
ignored.push(path.join(dir, entry));
}
return ret;
};
reader
.on('child', function (entry) {
nonIgnored.push(entry.path);
})
.on('error', deferred.reject)
.on('end', function () {
var promises;
// Ensure that we are not ignoring files that should not be ignored!
ignored = mout.array.unique(ignored);
ignored = ignored.filter(function (file) {
return nonIgnored.indexOf(file) === -1;
});
// Delete all the ignored files
promises = ignored.map(function (file) {
return Q.nfcall(rimraf, file);
});
return Q.all(promises)
.then(deferred.resolve, deferred.reject);
});
return deferred.promise;
}
module.exports = removeIgnores;
-32
View File
@@ -1,32 +0,0 @@
/*jshint multistr:true*/
'use strict';
var isRoot = require('is-root');
var createError = require('./createError');
var cli = require('./cli');
var renderer;
function rootCheck(options, config) {
var errorMsg;
// Allow running the command as root
if (options.allowRoot || config.allowRoot) {
return;
}
errorMsg = 'Since bower is a user command, there is no need to execute it with \
superuser permissions.\nIf you\'re having permission errors when using bower without \
sudo, please spend a few minutes learning more about how your system should work and \
make any necessary repairs.\n\n\
http://www.joyent.com/blog/installing-node-and-npm\n\
https://gist.github.com/isaacs/579814\n\n\
You can however run a command with sudo using --allow-root option';
if (isRoot()) {
renderer = cli.getRenderer('', false, config);
renderer.error(createError('Cannot be run with sudo', 'ESUDO', { details : errorMsg }));
process.exit(1);
}
}
module.exports = rootCheck;
-74
View File
@@ -1,74 +0,0 @@
var semver = require('semver');
var mout = require('mout');
function maxSatisfying(versions, range, strictMatch) {
var version;
var filteredVersions;
// Filter only valid versions, since semver.maxSatisfying() throws an error
versions = versions.filter(function (version) {
return semver.valid(version);
});
// Exact version & range match
if (semver.valid(range)) {
version = mout.array.find(versions, function (version) {
return version === range;
});
if (version) {
return version;
}
}
range = typeof range === 'string' ? range.trim() : range;
// When strict match is enabled give priority to non-pre-releases
// We do this by filtering every pre-release version
if (strictMatch) {
filteredVersions = versions.map(function (version) {
return !isPreRelease(version) ? version : null;
});
version = semver.maxSatisfying(filteredVersions, range);
if (version) {
return version;
}
}
// Fallback to regular semver max satisfies
return semver.maxSatisfying(versions, range);
}
function maxSatisfyingIndex(versions, range, strictMatch) {
var version = maxSatisfying(versions, range, strictMatch);
if (!version) {
return -1;
}
return versions.indexOf(version);
}
function clean(version) {
var parsed = semver.parse(version);
if (!parsed) {
return null;
}
// Keep builds!
return parsed.version + (parsed.build.length ? '+' + parsed.build.join('.') : '');
}
function isPreRelease(version) {
var parsed = semver.parse(version);
return parsed && parsed.prerelease && parsed.prerelease.length;
}
// Export a semver like object but with our custom functions
mout.object.mixIn(module.exports, semver, {
maxSatisfying: maxSatisfying,
maxSatisfyingIndex: maxSatisfyingIndex,
clean: clean,
valid: clean
});
-44
View File
@@ -1,44 +0,0 @@
var path = require('path');
var fs = require('graceful-fs');
var Handlebars = require('handlebars');
var mout = require('mout');
var helpers = require('../../templates/helpers');
var templatesDir = path.resolve(__dirname, '../../templates');
var cache = {};
// Register helpers
mout.object.forOwn(helpers, function (register) {
register(Handlebars);
});
function render(name, data, escape) {
var contents;
// Check if already compiled
if (cache[name]) {
return cache[name](data);
}
// Otherwise, read the file, compile and cache
contents = fs.readFileSync(path.join(templatesDir, name)).toString();
cache[name] = Handlebars.compile(contents, {
noEscape: !escape
});
// Call the function again
return render(name, data, escape);
}
function exists(name) {
if (cache[name]) {
return true;
}
return fs.existsSync(path.join(templatesDir, name));
}
module.exports = {
render: render,
exists: exists
};
-24
View File
@@ -1,24 +0,0 @@
var Q = require('q');
var fs = require('graceful-fs');
function validLink(file) {
// Ensures that a file is a symlink that points
// to a valid file
return Q.nfcall(fs.lstat, file)
.then(function (lstat) {
if (!lstat.isSymbolicLink()) {
return [false];
}
return Q.nfcall(fs.stat, file)
.then(function (stat) {
return [stat];
});
})
.fail(function (err) {
return [false, err];
});
}
module.exports = validLink;
-62
View File
@@ -1,62 +0,0 @@
module.exports = exports = abbrev.abbrev = abbrev
abbrev.monkeyPatch = monkeyPatch
function monkeyPatch () {
Object.defineProperty(Array.prototype, 'abbrev', {
value: function () { return abbrev(this) },
enumerable: false, configurable: true, writable: true
})
Object.defineProperty(Object.prototype, 'abbrev', {
value: function () { return abbrev(Object.keys(this)) },
enumerable: false, configurable: true, writable: true
})
}
function abbrev (list) {
if (arguments.length !== 1 || !Array.isArray(list)) {
list = Array.prototype.slice.call(arguments, 0)
}
for (var i = 0, l = list.length, args = [] ; i < l ; i ++) {
args[i] = typeof list[i] === "string" ? list[i] : String(list[i])
}
// sort them lexicographically, so that they're next to their nearest kin
args = args.sort(lexSort)
// walk through each, seeing how much it has in common with the next and previous
var abbrevs = {}
, prev = ""
for (var i = 0, l = args.length ; i < l ; i ++) {
var current = args[i]
, next = args[i + 1] || ""
, nextMatches = true
, prevMatches = true
if (current === next) continue
for (var j = 0, cl = current.length ; j < cl ; j ++) {
var curChar = current.charAt(j)
nextMatches = nextMatches && curChar === next.charAt(j)
prevMatches = prevMatches && curChar === prev.charAt(j)
if (!nextMatches && !prevMatches) {
j ++
break
}
}
prev = current
if (j === cl) {
abbrevs[current] = current
continue
}
for (var a = current.substr(0, j) ; j <= cl ; j ++) {
abbrevs[a] = current
a += current.charAt(j)
}
}
return abbrevs
}
function lexSort (a, b) {
return a === b ? 0 : a > b ? 1 : -1
}
@@ -1,46 +0,0 @@
{
"name": "abbrev",
"version": "1.0.5",
"description": "Like ruby's abbrev module, but in js",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me"
},
"main": "abbrev.js",
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "http://github.com/isaacs/abbrev-js"
},
"license": {
"type": "MIT",
"url": "https://github.com/isaacs/abbrev-js/raw/master/LICENSE"
},
"bugs": {
"url": "https://github.com/isaacs/abbrev-js/issues"
},
"homepage": "https://github.com/isaacs/abbrev-js",
"_id": "abbrev@1.0.5",
"_shasum": "5d8257bd9ebe435e698b2fa431afde4fe7b10b03",
"_from": "abbrev@~1.0.4",
"_npmVersion": "1.4.7",
"_npmUser": {
"name": "isaacs",
"email": "i@izs.me"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"dist": {
"shasum": "5d8257bd9ebe435e698b2fa431afde4fe7b10b03",
"tarball": "http://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz",
"readme": "ERROR: No README data found!"
}
-35
View File
@@ -1,35 +0,0 @@
module.exports = function archy (obj, prefix, opts) {
if (prefix === undefined) prefix = '';
if (!opts) opts = {};
var chr = function (s) {
var chars = {
'│' : '|',
'└' : '`',
'├' : '+',
'─' : '-',
'┬' : '-'
};
return opts.unicode === false ? chars[s] : s;
};
if (typeof obj === 'string') obj = { label : obj };
var nodes = obj.nodes || [];
var lines = (obj.label || '').split('\n');
var splitter = '\n' + prefix + (nodes.length ? chr('│') : ' ') + ' ';
return prefix
+ lines.join(splitter) + '\n'
+ nodes.map(function (node, ix) {
var last = ix === nodes.length - 1;
var more = node.nodes && node.nodes.length;
var prefix_ = prefix + (last ? ' ' : chr('│')) + ' ';
return prefix
+ (last ? chr('└') : chr('├')) + chr('─')
+ (more ? chr('┬') : chr('─')) + ' '
+ archy(node, prefix_, opts).slice(prefix.length + 2)
;
}).join('')
;
};
@@ -1,69 +0,0 @@
{
"name": "archy",
"version": "0.0.2",
"description": "render nested hierarchies `npm ls` style with unicode pipes",
"main": "index.js",
"directories": {
"lib": ".",
"example": "example",
"test": "test"
},
"devDependencies": {
"tap": "~0.2.3"
},
"scripts": {
"test": "tap test"
},
"repository": {
"type": "git",
"url": "git://github.com/substack/node-archy.git"
},
"keywords": [
"hierarchy",
"npm ls",
"unicode",
"pretty",
"print"
],
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"license": "MIT/X11",
"engine": {
"node": ">=0.4"
},
"_npmUser": {
"name": "substack",
"email": "mail@substack.net"
},
"_id": "archy@0.0.2",
"dependencies": {},
"engines": {
"node": "*"
},
"_engineSupported": true,
"_npmVersion": "1.0.106",
"_nodeVersion": "v0.4.12",
"_defaultsLoaded": true,
"dist": {
"shasum": "910f43bf66141fc335564597abc189df44b3d35e",
"tarball": "http://registry.npmjs.org/archy/-/archy-0.0.2.tgz"
},
"maintainers": [
{
"name": "substack",
"email": "mail@substack.net"
}
],
"_shasum": "910f43bf66141fc335564597abc189df44b3d35e",
"_from": "archy@0.0.2",
"_resolved": "https://registry.npmjs.org/archy/-/archy-0.0.2.tgz",
"readme": "archy\n=====\n\nRender nested hierarchies `npm ls` style with unicode pipes.\n\n[![build status](https://secure.travis-ci.org/substack/node-archy.png)](http://travis-ci.org/substack/node-archy)\n\nexample\n=======\n\n``` js\nvar archy = require('archy');\nvar s = archy({\n label : 'beep',\n nodes : [\n 'ity',\n {\n label : 'boop',\n nodes : [\n {\n label : 'o_O',\n nodes : [\n {\n label : 'oh',\n nodes : [ 'hello', 'puny' ]\n },\n 'human'\n ]\n },\n 'party\\ntime!'\n ]\n }\n ]\n});\nconsole.log(s);\n```\n\noutput\n\n```\nbeep\n├── ity\n└─┬ boop\n ├─┬ o_O\n │ ├─┬ oh\n │ │ ├── hello\n │ │ └── puny\n │ └── human\n └── party\n time!\n```\n\nmethods\n=======\n\nvar archy = require('archy')\n\narchy(obj, prefix='', opts={})\n------------------------------\n\nReturn a string representation of `obj` with unicode pipe characters like how\n`npm ls` looks.\n\n`obj` should be a tree of nested objects with `'label'` and `'nodes'` fields.\n`'label'` is a string of text to display at a node level and `'nodes'` is an\narray of the descendents of the current node.\n\nIf a node is a string, that string will be used as the `'label'` and an empty\narray of `'nodes'` will be used.\n\n`prefix` gets prepended to all the lines and is used by the algorithm to\nrecursively update.\n\nIf `'label'` has newlines they will be indented at the present indentation level\nwith the current prefix.\n\nTo disable unicode results in favor of all-ansi output set `opts.unicode` to\n`false`.\n\ninstall\n=======\n\nWith [npm](http://npmjs.org) do:\n\n```\nnpm install archy\n```\n\nlicense\n=======\n\nMIT/X11\n",
"readmeFilename": "README.markdown",
"bugs": {
"url": "https://github.com/substack/node-archy/issues"
},
"homepage": "https://github.com/substack/node-archy"
}
-216
View File
@@ -1,216 +0,0 @@
var DuplexStream = require('readable-stream').Duplex
, util = require('util')
function BufferList (callback) {
if (!(this instanceof BufferList))
return new BufferList(callback)
this._bufs = []
this.length = 0
if (typeof callback == 'function') {
this._callback = callback
var piper = function (err) {
if (this._callback) {
this._callback(err)
this._callback = null
}
}.bind(this)
this.on('pipe', function (src) {
src.on('error', piper)
})
this.on('unpipe', function (src) {
src.removeListener('error', piper)
})
}
else if (Buffer.isBuffer(callback))
this.append(callback)
else if (Array.isArray(callback)) {
callback.forEach(function (b) {
Buffer.isBuffer(b) && this.append(b)
}.bind(this))
}
DuplexStream.call(this)
}
util.inherits(BufferList, DuplexStream)
BufferList.prototype._offset = function (offset) {
var tot = 0, i = 0, _t
for (; i < this._bufs.length; i++) {
_t = tot + this._bufs[i].length
if (offset < _t)
return [ i, offset - tot ]
tot = _t
}
}
BufferList.prototype.append = function (buf) {
var isBuffer = Buffer.isBuffer(buf) ||
buf instanceof BufferList
this._bufs.push(isBuffer ? buf : new Buffer(buf))
this.length += buf.length
return this
}
BufferList.prototype._write = function (buf, encoding, callback) {
this.append(buf)
if (callback)
callback()
}
BufferList.prototype._read = function (size) {
if (!this.length)
return this.push(null)
size = Math.min(size, this.length)
this.push(this.slice(0, size))
this.consume(size)
}
BufferList.prototype.end = function (chunk) {
DuplexStream.prototype.end.call(this, chunk)
if (this._callback) {
this._callback(null, this.slice())
this._callback = null
}
}
BufferList.prototype.get = function (index) {
return this.slice(index, index + 1)[0]
}
BufferList.prototype.slice = function (start, end) {
return this.copy(null, 0, start, end)
}
BufferList.prototype.copy = function (dst, dstStart, srcStart, srcEnd) {
if (typeof srcStart != 'number' || srcStart < 0)
srcStart = 0
if (typeof srcEnd != 'number' || srcEnd > this.length)
srcEnd = this.length
if (srcStart >= this.length)
return dst || new Buffer(0)
if (srcEnd <= 0)
return dst || new Buffer(0)
var copy = !!dst
, off = this._offset(srcStart)
, len = srcEnd - srcStart
, bytes = len
, bufoff = (copy && dstStart) || 0
, start = off[1]
, l
, i
// copy/slice everything
if (srcStart === 0 && srcEnd == this.length) {
if (!copy) // slice, just return a full concat
return Buffer.concat(this._bufs)
// copy, need to copy individual buffers
for (i = 0; i < this._bufs.length; i++) {
this._bufs[i].copy(dst, bufoff)
bufoff += this._bufs[i].length
}
return dst
}
// easy, cheap case where it's a subset of one of the buffers
if (bytes <= this._bufs[off[0]].length - start) {
return copy
? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes)
: this._bufs[off[0]].slice(start, start + bytes)
}
if (!copy) // a slice, we need something to copy in to
dst = new Buffer(len)
for (i = off[0]; i < this._bufs.length; i++) {
l = this._bufs[i].length - start
if (bytes > l) {
this._bufs[i].copy(dst, bufoff, start)
} else {
this._bufs[i].copy(dst, bufoff, start, start + bytes)
break
}
bufoff += l
bytes -= l
if (start)
start = 0
}
return dst
}
BufferList.prototype.toString = function (encoding, start, end) {
return this.slice(start, end).toString(encoding)
}
BufferList.prototype.consume = function (bytes) {
while (this._bufs.length) {
if (bytes > this._bufs[0].length) {
bytes -= this._bufs[0].length
this.length -= this._bufs[0].length
this._bufs.shift()
} else {
this._bufs[0] = this._bufs[0].slice(bytes)
this.length -= bytes
break
}
}
return this
}
BufferList.prototype.duplicate = function () {
var i = 0
, copy = new BufferList()
for (; i < this._bufs.length; i++)
copy.append(this._bufs[i])
return copy
}
BufferList.prototype.destroy = function () {
this._bufs.length = 0;
this.length = 0;
this.push(null);
}
;(function () {
var methods = {
'readDoubleBE' : 8
, 'readDoubleLE' : 8
, 'readFloatBE' : 4
, 'readFloatLE' : 4
, 'readInt32BE' : 4
, 'readInt32LE' : 4
, 'readUInt32BE' : 4
, 'readUInt32LE' : 4
, 'readInt16BE' : 2
, 'readInt16LE' : 2
, 'readUInt16BE' : 2
, 'readUInt16LE' : 2
, 'readInt8' : 1
, 'readUInt8' : 1
}
for (var m in methods) {
(function (m) {
BufferList.prototype[m] = function (offset) {
return this.slice(offset, offset + methods[m])[m](0)
}
}(m))
}
}())
module.exports = BufferList
@@ -1 +0,0 @@
module.exports = require("./lib/_stream_duplex.js")
@@ -1,89 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
module.exports = Duplex;
/*<replacement>*/
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
}
/*</replacement>*/
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Readable = require('./_stream_readable');
var Writable = require('./_stream_writable');
util.inherits(Duplex, Readable);
forEach(objectKeys(Writable.prototype), function(method) {
if (!Duplex.prototype[method])
Duplex.prototype[method] = Writable.prototype[method];
});
function Duplex(options) {
if (!(this instanceof Duplex))
return new Duplex(options);
Readable.call(this, options);
Writable.call(this, options);
if (options && options.readable === false)
this.readable = false;
if (options && options.writable === false)
this.writable = false;
this.allowHalfOpen = true;
if (options && options.allowHalfOpen === false)
this.allowHalfOpen = false;
this.once('end', onend);
}
// the no-half-open enforcer
function onend() {
// if we allow half-open state, or if the writable side ended,
// then we're ok.
if (this.allowHalfOpen || this._writableState.ended)
return;
// no more data can be written.
// But allow more writes to happen in this tick.
process.nextTick(this.end.bind(this));
}
function forEach (xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
@@ -1,46 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
module.exports = PassThrough;
var Transform = require('./_stream_transform');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(PassThrough, Transform);
function PassThrough(options) {
if (!(this instanceof PassThrough))
return new PassThrough(options);
Transform.call(this, options);
}
PassThrough.prototype._transform = function(chunk, encoding, cb) {
cb(null, chunk);
};
@@ -1,982 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
module.exports = Readable;
/*<replacement>*/
var isArray = require('isarray');
/*</replacement>*/
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Readable.ReadableState = ReadableState;
var EE = require('events').EventEmitter;
/*<replacement>*/
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
return emitter.listeners(type).length;
};
/*</replacement>*/
var Stream = require('stream');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var StringDecoder;
util.inherits(Readable, Stream);
function ReadableState(options, stream) {
options = options || {};
// the point at which it stops calling _read() to fill the buffer
// Note: 0 is a valid value, means "don't call _read preemptively ever"
var hwm = options.highWaterMark;
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.buffer = [];
this.length = 0;
this.pipes = null;
this.pipesCount = 0;
this.flowing = false;
this.ended = false;
this.endEmitted = false;
this.reading = false;
// In streams that never have any data, and do push(null) right away,
// the consumer can miss the 'end' event if they do some I/O before
// consuming the stream. So, we don't emit('end') until some reading
// happens.
this.calledRead = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, becuase any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// whenever we return null, then we set a flag to say
// that we're awaiting a 'readable' event emission.
this.needReadable = false;
this.emittedReadable = false;
this.readableListening = false;
// object stream flag. Used to make read(n) ignore n and to
// make all the buffer merging and length checks go away
this.objectMode = !!options.objectMode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// when piping, we only care about 'readable' events that happen
// after read()ing all the bytes and not getting any pushback.
this.ranOut = false;
// the number of writers that are awaiting a drain event in .pipe()s
this.awaitDrain = 0;
// if true, a maybeReadMore has been scheduled
this.readingMore = false;
this.decoder = null;
this.encoding = null;
if (options.encoding) {
if (!StringDecoder)
StringDecoder = require('string_decoder/').StringDecoder;
this.decoder = new StringDecoder(options.encoding);
this.encoding = options.encoding;
}
}
function Readable(options) {
if (!(this instanceof Readable))
return new Readable(options);
this._readableState = new ReadableState(options, this);
// legacy
this.readable = true;
Stream.call(this);
}
// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function(chunk, encoding) {
var state = this._readableState;
if (typeof chunk === 'string' && !state.objectMode) {
encoding = encoding || state.defaultEncoding;
if (encoding !== state.encoding) {
chunk = new Buffer(chunk, encoding);
encoding = '';
}
}
return readableAddChunk(this, state, chunk, encoding, false);
};
// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function(chunk) {
var state = this._readableState;
return readableAddChunk(this, state, chunk, '', true);
};
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
var er = chunkInvalid(state, chunk);
if (er) {
stream.emit('error', er);
} else if (chunk === null || chunk === undefined) {
state.reading = false;
if (!state.ended)
onEofChunk(stream, state);
} else if (state.objectMode || chunk && chunk.length > 0) {
if (state.ended && !addToFront) {
var e = new Error('stream.push() after EOF');
stream.emit('error', e);
} else if (state.endEmitted && addToFront) {
var e = new Error('stream.unshift() after end event');
stream.emit('error', e);
} else {
if (state.decoder && !addToFront && !encoding)
chunk = state.decoder.write(chunk);
// update the buffer info.
state.length += state.objectMode ? 1 : chunk.length;
if (addToFront) {
state.buffer.unshift(chunk);
} else {
state.reading = false;
state.buffer.push(chunk);
}
if (state.needReadable)
emitReadable(stream);
maybeReadMore(stream, state);
}
} else if (!addToFront) {
state.reading = false;
}
return needMoreData(state);
}
// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes. This is to work around cases where hwm=0,
// such as the repl. Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
return !state.ended &&
(state.needReadable ||
state.length < state.highWaterMark ||
state.length === 0);
}
// backwards compatibility.
Readable.prototype.setEncoding = function(enc) {
if (!StringDecoder)
StringDecoder = require('string_decoder/').StringDecoder;
this._readableState.decoder = new StringDecoder(enc);
this._readableState.encoding = enc;
};
// Don't raise the hwm > 128MB
var MAX_HWM = 0x800000;
function roundUpToNextPowerOf2(n) {
if (n >= MAX_HWM) {
n = MAX_HWM;
} else {
// Get the next highest power of 2
n--;
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
n++;
}
return n;
}
function howMuchToRead(n, state) {
if (state.length === 0 && state.ended)
return 0;
if (state.objectMode)
return n === 0 ? 0 : 1;
if (n === null || isNaN(n)) {
// only flow one buffer at a time
if (state.flowing && state.buffer.length)
return state.buffer[0].length;
else
return state.length;
}
if (n <= 0)
return 0;
// If we're asking for more than the target buffer level,
// then raise the water mark. Bump up to the next highest
// power of 2, to prevent increasing it excessively in tiny
// amounts.
if (n > state.highWaterMark)
state.highWaterMark = roundUpToNextPowerOf2(n);
// don't have that much. return null, unless we've ended.
if (n > state.length) {
if (!state.ended) {
state.needReadable = true;
return 0;
} else
return state.length;
}
return n;
}
// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function(n) {
var state = this._readableState;
state.calledRead = true;
var nOrig = n;
var ret;
if (typeof n !== 'number' || n > 0)
state.emittedReadable = false;
// if we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
// the 'readable' event and move on.
if (n === 0 &&
state.needReadable &&
(state.length >= state.highWaterMark || state.ended)) {
emitReadable(this);
return null;
}
n = howMuchToRead(n, state);
// if we've ended, and we're now clear, then finish it up.
if (n === 0 && state.ended) {
ret = null;
// In cases where the decoder did not receive enough data
// to produce a full chunk, then immediately received an
// EOF, state.buffer will contain [<Buffer >, <Buffer 00 ...>].
// howMuchToRead will see this and coerce the amount to
// read to zero (because it's looking at the length of the
// first <Buffer > in state.buffer), and we'll end up here.
//
// This can only happen via state.decoder -- no other venue
// exists for pushing a zero-length chunk into state.buffer
// and triggering this behavior. In this case, we return our
// remaining data and end the stream, if appropriate.
if (state.length > 0 && state.decoder) {
ret = fromList(n, state);
state.length -= ret.length;
}
if (state.length === 0)
endReadable(this);
return ret;
}
// All the actual chunk generation logic needs to be
// *below* the call to _read. The reason is that in certain
// synthetic stream cases, such as passthrough streams, _read
// may be a completely synchronous operation which may change
// the state of the read buffer, providing enough data when
// before there was *not* enough.
//
// So, the steps are:
// 1. Figure out what the state of things will be after we do
// a read from the buffer.
//
// 2. If that resulting state will trigger a _read, then call _read.
// Note that this may be asynchronous, or synchronous. Yes, it is
// deeply ugly to write APIs this way, but that still doesn't mean
// that the Readable class should behave improperly, as streams are
// designed to be sync/async agnostic.
// Take note if the _read call is sync or async (ie, if the read call
// has returned yet), so that we know whether or not it's safe to emit
// 'readable' etc.
//
// 3. Actually pull the requested chunks out of the buffer and return.
// if we need a readable event, then we need to do some reading.
var doRead = state.needReadable;
// if we currently have less than the highWaterMark, then also read some
if (state.length - n <= state.highWaterMark)
doRead = true;
// however, if we've ended, then there's no point, and if we're already
// reading, then it's unnecessary.
if (state.ended || state.reading)
doRead = false;
if (doRead) {
state.reading = true;
state.sync = true;
// if the length is currently zero, then we *need* a readable event.
if (state.length === 0)
state.needReadable = true;
// call internal read method
this._read(state.highWaterMark);
state.sync = false;
}
// If _read called its callback synchronously, then `reading`
// will be false, and we need to re-evaluate how much data we
// can return to the user.
if (doRead && !state.reading)
n = howMuchToRead(nOrig, state);
if (n > 0)
ret = fromList(n, state);
else
ret = null;
if (ret === null) {
state.needReadable = true;
n = 0;
}
state.length -= n;
// If we have nothing in the buffer, then we want to know
// as soon as we *do* get something into the buffer.
if (state.length === 0 && !state.ended)
state.needReadable = true;
// If we happened to read() exactly the remaining amount in the
// buffer, and the EOF has been seen at this point, then make sure
// that we emit 'end' on the very next tick.
if (state.ended && !state.endEmitted && state.length === 0)
endReadable(this);
return ret;
};
function chunkInvalid(state, chunk) {
var er = null;
if (!Buffer.isBuffer(chunk) &&
'string' !== typeof chunk &&
chunk !== null &&
chunk !== undefined &&
!state.objectMode) {
er = new TypeError('Invalid non-string/buffer chunk');
}
return er;
}
function onEofChunk(stream, state) {
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length) {
state.buffer.push(chunk);
state.length += state.objectMode ? 1 : chunk.length;
}
}
state.ended = true;
// if we've ended and we have some data left, then emit
// 'readable' now to make sure it gets picked up.
if (state.length > 0)
emitReadable(stream);
else
endReadable(stream);
}
// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow. This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
var state = stream._readableState;
state.needReadable = false;
if (state.emittedReadable)
return;
state.emittedReadable = true;
if (state.sync)
process.nextTick(function() {
emitReadable_(stream);
});
else
emitReadable_(stream);
}
function emitReadable_(stream) {
stream.emit('readable');
}
// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data. that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
if (!state.readingMore) {
state.readingMore = true;
process.nextTick(function() {
maybeReadMore_(stream, state);
});
}
}
function maybeReadMore_(stream, state) {
var len = state.length;
while (!state.reading && !state.flowing && !state.ended &&
state.length < state.highWaterMark) {
stream.read(0);
if (len === state.length)
// didn't get any data, stop spinning.
break;
else
len = state.length;
}
state.readingMore = false;
}
// abstract method. to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function(n) {
this.emit('error', new Error('not implemented'));
};
Readable.prototype.pipe = function(dest, pipeOpts) {
var src = this;
var state = this._readableState;
switch (state.pipesCount) {
case 0:
state.pipes = dest;
break;
case 1:
state.pipes = [state.pipes, dest];
break;
default:
state.pipes.push(dest);
break;
}
state.pipesCount += 1;
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
dest !== process.stdout &&
dest !== process.stderr;
var endFn = doEnd ? onend : cleanup;
if (state.endEmitted)
process.nextTick(endFn);
else
src.once('end', endFn);
dest.on('unpipe', onunpipe);
function onunpipe(readable) {
if (readable !== src) return;
cleanup();
}
function onend() {
dest.end();
}
// when the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
var ondrain = pipeOnDrain(src);
dest.on('drain', ondrain);
function cleanup() {
// cleanup event handlers once the pipe is broken
dest.removeListener('close', onclose);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', onunpipe);
src.removeListener('end', onend);
src.removeListener('end', cleanup);
// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (!dest._writableState || dest._writableState.needDrain)
ondrain();
}
// if the dest has an error, then stop piping into it.
// however, don't suppress the throwing behavior for this.
function onerror(er) {
unpipe();
dest.removeListener('error', onerror);
if (EE.listenerCount(dest, 'error') === 0)
dest.emit('error', er);
}
// This is a brutally ugly hack to make sure that our error handler
// is attached before any userland ones. NEVER DO THIS.
if (!dest._events || !dest._events.error)
dest.on('error', onerror);
else if (isArray(dest._events.error))
dest._events.error.unshift(onerror);
else
dest._events.error = [onerror, dest._events.error];
// Both close and finish should trigger unpipe, but only once.
function onclose() {
dest.removeListener('finish', onfinish);
unpipe();
}
dest.once('close', onclose);
function onfinish() {
dest.removeListener('close', onclose);
unpipe();
}
dest.once('finish', onfinish);
function unpipe() {
src.unpipe(dest);
}
// tell the dest that it's being piped to
dest.emit('pipe', src);
// start the flow if it hasn't been started already.
if (!state.flowing) {
// the handler that waits for readable events after all
// the data gets sucked out in flow.
// This would be easier to follow with a .once() handler
// in flow(), but that is too slow.
this.on('readable', pipeOnReadable);
state.flowing = true;
process.nextTick(function() {
flow(src);
});
}
return dest;
};
function pipeOnDrain(src) {
return function() {
var dest = this;
var state = src._readableState;
state.awaitDrain--;
if (state.awaitDrain === 0)
flow(src);
};
}
function flow(src) {
var state = src._readableState;
var chunk;
state.awaitDrain = 0;
function write(dest, i, list) {
var written = dest.write(chunk);
if (false === written) {
state.awaitDrain++;
}
}
while (state.pipesCount && null !== (chunk = src.read())) {
if (state.pipesCount === 1)
write(state.pipes, 0, null);
else
forEach(state.pipes, write);
src.emit('data', chunk);
// if anyone needs a drain, then we have to wait for that.
if (state.awaitDrain > 0)
return;
}
// if every destination was unpiped, either before entering this
// function, or in the while loop, then stop flowing.
//
// NB: This is a pretty rare edge case.
if (state.pipesCount === 0) {
state.flowing = false;
// if there were data event listeners added, then switch to old mode.
if (EE.listenerCount(src, 'data') > 0)
emitDataEvents(src);
return;
}
// at this point, no one needed a drain, so we just ran out of data
// on the next readable event, start it over again.
state.ranOut = true;
}
function pipeOnReadable() {
if (this._readableState.ranOut) {
this._readableState.ranOut = false;
flow(this);
}
}
Readable.prototype.unpipe = function(dest) {
var state = this._readableState;
// if we're not piping anywhere, then do nothing.
if (state.pipesCount === 0)
return this;
// just one destination. most common case.
if (state.pipesCount === 1) {
// passed in one, but it's not the right one.
if (dest && dest !== state.pipes)
return this;
if (!dest)
dest = state.pipes;
// got a match.
state.pipes = null;
state.pipesCount = 0;
this.removeListener('readable', pipeOnReadable);
state.flowing = false;
if (dest)
dest.emit('unpipe', this);
return this;
}
// slow case. multiple pipe destinations.
if (!dest) {
// remove all.
var dests = state.pipes;
var len = state.pipesCount;
state.pipes = null;
state.pipesCount = 0;
this.removeListener('readable', pipeOnReadable);
state.flowing = false;
for (var i = 0; i < len; i++)
dests[i].emit('unpipe', this);
return this;
}
// try to find the right one.
var i = indexOf(state.pipes, dest);
if (i === -1)
return this;
state.pipes.splice(i, 1);
state.pipesCount -= 1;
if (state.pipesCount === 1)
state.pipes = state.pipes[0];
dest.emit('unpipe', this);
return this;
};
// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function(ev, fn) {
var res = Stream.prototype.on.call(this, ev, fn);
if (ev === 'data' && !this._readableState.flowing)
emitDataEvents(this);
if (ev === 'readable' && this.readable) {
var state = this._readableState;
if (!state.readableListening) {
state.readableListening = true;
state.emittedReadable = false;
state.needReadable = true;
if (!state.reading) {
this.read(0);
} else if (state.length) {
emitReadable(this, state);
}
}
}
return res;
};
Readable.prototype.addListener = Readable.prototype.on;
// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function() {
emitDataEvents(this);
this.read(0);
this.emit('resume');
};
Readable.prototype.pause = function() {
emitDataEvents(this, true);
this.emit('pause');
};
function emitDataEvents(stream, startPaused) {
var state = stream._readableState;
if (state.flowing) {
// https://github.com/isaacs/readable-stream/issues/16
throw new Error('Cannot switch to old mode now.');
}
var paused = startPaused || false;
var readable = false;
// convert to an old-style stream.
stream.readable = true;
stream.pipe = Stream.prototype.pipe;
stream.on = stream.addListener = Stream.prototype.on;
stream.on('readable', function() {
readable = true;
var c;
while (!paused && (null !== (c = stream.read())))
stream.emit('data', c);
if (c === null) {
readable = false;
stream._readableState.needReadable = true;
}
});
stream.pause = function() {
paused = true;
this.emit('pause');
};
stream.resume = function() {
paused = false;
if (readable)
process.nextTick(function() {
stream.emit('readable');
});
else
this.read(0);
this.emit('resume');
};
// now make it start, just in case it hadn't already.
stream.emit('readable');
}
// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function(stream) {
var state = this._readableState;
var paused = false;
var self = this;
stream.on('end', function() {
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length)
self.push(chunk);
}
self.push(null);
});
stream.on('data', function(chunk) {
if (state.decoder)
chunk = state.decoder.write(chunk);
// don't skip over falsy values in objectMode
//if (state.objectMode && util.isNullOrUndefined(chunk))
if (state.objectMode && (chunk === null || chunk === undefined))
return;
else if (!state.objectMode && (!chunk || !chunk.length))
return;
var ret = self.push(chunk);
if (!ret) {
paused = true;
stream.pause();
}
});
// proxy all the other methods.
// important when wrapping filters and duplexes.
for (var i in stream) {
if (typeof stream[i] === 'function' &&
typeof this[i] === 'undefined') {
this[i] = function(method) { return function() {
return stream[method].apply(stream, arguments);
}}(i);
}
}
// proxy certain important events.
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
forEach(events, function(ev) {
stream.on(ev, self.emit.bind(self, ev));
});
// when we try to consume some more bytes, simply unpause the
// underlying stream.
self._read = function(n) {
if (paused) {
paused = false;
stream.resume();
}
};
return self;
};
// exposed for testing purposes only.
Readable._fromList = fromList;
// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
var list = state.buffer;
var length = state.length;
var stringMode = !!state.decoder;
var objectMode = !!state.objectMode;
var ret;
// nothing in the list, definitely empty.
if (list.length === 0)
return null;
if (length === 0)
ret = null;
else if (objectMode)
ret = list.shift();
else if (!n || n >= length) {
// read it all, truncate the array.
if (stringMode)
ret = list.join('');
else
ret = Buffer.concat(list, length);
list.length = 0;
} else {
// read just some of it.
if (n < list[0].length) {
// just take a part of the first list item.
// slice is the same for buffers and strings.
var buf = list[0];
ret = buf.slice(0, n);
list[0] = buf.slice(n);
} else if (n === list[0].length) {
// first list is a perfect match
ret = list.shift();
} else {
// complex case.
// we have enough to cover it, but it spans past the first buffer.
if (stringMode)
ret = '';
else
ret = new Buffer(n);
var c = 0;
for (var i = 0, l = list.length; i < l && c < n; i++) {
var buf = list[0];
var cpy = Math.min(n - c, buf.length);
if (stringMode)
ret += buf.slice(0, cpy);
else
buf.copy(ret, c, 0, cpy);
if (cpy < buf.length)
list[0] = buf.slice(cpy);
else
list.shift();
c += cpy;
}
}
}
return ret;
}
function endReadable(stream) {
var state = stream._readableState;
// If we get here before consuming all the bytes, then that is a
// bug in node. Should never happen.
if (state.length > 0)
throw new Error('endReadable called on non-empty stream');
if (!state.endEmitted && state.calledRead) {
state.ended = true;
process.nextTick(function() {
// Check that we didn't get one last unshift.
if (!state.endEmitted && state.length === 0) {
state.endEmitted = true;
stream.readable = false;
stream.emit('end');
}
});
}
}
function forEach (xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
function indexOf (xs, x) {
for (var i = 0, l = xs.length; i < l; i++) {
if (xs[i] === x) return i;
}
return -1;
}
@@ -1,210 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a transform stream is a readable/writable stream where you do
// something with the data. Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored. (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation. For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes. When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up. When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer. When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks. If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk. However,
// a pathological inflate type of transform can cause excessive buffering
// here. For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output. In this case, you could write a very small
// amount of input, and end up with a very large amount of output. In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform. A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
module.exports = Transform;
var Duplex = require('./_stream_duplex');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(Transform, Duplex);
function TransformState(options, stream) {
this.afterTransform = function(er, data) {
return afterTransform(stream, er, data);
};
this.needTransform = false;
this.transforming = false;
this.writecb = null;
this.writechunk = null;
}
function afterTransform(stream, er, data) {
var ts = stream._transformState;
ts.transforming = false;
var cb = ts.writecb;
if (!cb)
return stream.emit('error', new Error('no writecb in Transform class'));
ts.writechunk = null;
ts.writecb = null;
if (data !== null && data !== undefined)
stream.push(data);
if (cb)
cb(er);
var rs = stream._readableState;
rs.reading = false;
if (rs.needReadable || rs.length < rs.highWaterMark) {
stream._read(rs.highWaterMark);
}
}
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
var ts = this._transformState = new TransformState(options, this);
// when the writable side finishes, then flush out anything remaining.
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
this.once('finish', function() {
if ('function' === typeof this._flush)
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}
Transform.prototype.push = function(chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
};
// This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side. You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk. If you pass
// an error, then that'll put the hurt on the whole operation. If you
// never call cb(), then you'll never get another chunk.
Transform.prototype._transform = function(chunk, encoding, cb) {
throw new Error('not implemented');
};
Transform.prototype._write = function(chunk, encoding, cb) {
var ts = this._transformState;
ts.writecb = cb;
ts.writechunk = chunk;
ts.writeencoding = encoding;
if (!ts.transforming) {
var rs = this._readableState;
if (ts.needTransform ||
rs.needReadable ||
rs.length < rs.highWaterMark)
this._read(rs.highWaterMark);
}
};
// Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.
Transform.prototype._read = function(n) {
var ts = this._transformState;
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
};
function done(stream, er) {
if (er)
return stream.emit('error', er);
// if there's nothing in the write buffer, then that means
// that nothing more will ever be provided
var ws = stream._writableState;
var rs = stream._readableState;
var ts = stream._transformState;
if (ws.length)
throw new Error('calling transform done when ws.length != 0');
if (ts.transforming)
throw new Error('calling transform done when still transforming');
return stream.push(null);
}
@@ -1,386 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// A bit simpler than readable streams.
// Implement an async ._write(chunk, cb), and it'll handle all
// the drain event emission and buffering.
module.exports = Writable;
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Writable.WritableState = WritableState;
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Stream = require('stream');
util.inherits(Writable, Stream);
function WriteReq(chunk, encoding, cb) {
this.chunk = chunk;
this.encoding = encoding;
this.callback = cb;
}
function WritableState(options, stream) {
options = options || {};
// the point at which write() starts returning false
// Note: 0 is a valid value, means that we always return false if
// the entire buffer is not flushed immediately on write()
var hwm = options.highWaterMark;
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
// object stream flag to indicate whether or not this stream
// contains buffers or objects.
this.objectMode = !!options.objectMode;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.needDrain = false;
// at the start of calling end()
this.ending = false;
// when end() has been called, and returned
this.ended = false;
// when 'finish' is emitted
this.finished = false;
// should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
// handling at a lower level.
var noDecode = options.decodeStrings === false;
this.decodeStrings = !noDecode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// not an actual buffer we keep track of, but a measurement
// of how much we're waiting to get pushed to some underlying
// socket or file.
this.length = 0;
// a flag to see when we're in the middle of a write.
this.writing = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, becuase any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// a flag to know if we're processing previously buffered items, which
// may call the _write() callback in the same tick, so that we don't
// end up in an overlapped onwrite situation.
this.bufferProcessing = false;
// the callback that's passed to _write(chunk,cb)
this.onwrite = function(er) {
onwrite(stream, er);
};
// the callback that the user supplies to write(chunk,encoding,cb)
this.writecb = null;
// the amount that is being written when _write is called.
this.writelen = 0;
this.buffer = [];
// True if the error was already emitted and should not be thrown again
this.errorEmitted = false;
}
function Writable(options) {
var Duplex = require('./_stream_duplex');
// Writable ctor is applied to Duplexes, though they're not
// instanceof Writable, they're instanceof Readable.
if (!(this instanceof Writable) && !(this instanceof Duplex))
return new Writable(options);
this._writableState = new WritableState(options, this);
// legacy.
this.writable = true;
Stream.call(this);
}
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function() {
this.emit('error', new Error('Cannot pipe. Not readable.'));
};
function writeAfterEnd(stream, state, cb) {
var er = new Error('write after end');
// TODO: defer error events consistently everywhere, not just the cb
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
}
// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
var valid = true;
if (!Buffer.isBuffer(chunk) &&
'string' !== typeof chunk &&
chunk !== null &&
chunk !== undefined &&
!state.objectMode) {
var er = new TypeError('Invalid non-string/buffer chunk');
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
valid = false;
}
return valid;
}
Writable.prototype.write = function(chunk, encoding, cb) {
var state = this._writableState;
var ret = false;
if (typeof encoding === 'function') {
cb = encoding;
encoding = null;
}
if (Buffer.isBuffer(chunk))
encoding = 'buffer';
else if (!encoding)
encoding = state.defaultEncoding;
if (typeof cb !== 'function')
cb = function() {};
if (state.ended)
writeAfterEnd(this, state, cb);
else if (validChunk(this, state, chunk, cb))
ret = writeOrBuffer(this, state, chunk, encoding, cb);
return ret;
};
function decodeChunk(state, chunk, encoding) {
if (!state.objectMode &&
state.decodeStrings !== false &&
typeof chunk === 'string') {
chunk = new Buffer(chunk, encoding);
}
return chunk;
}
// if we're already writing something, then just put this
// in the queue, and wait our turn. Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
chunk = decodeChunk(state, chunk, encoding);
if (Buffer.isBuffer(chunk))
encoding = 'buffer';
var len = state.objectMode ? 1 : chunk.length;
state.length += len;
var ret = state.length < state.highWaterMark;
// we must ensure that previous needDrain will not be reset to false.
if (!ret)
state.needDrain = true;
if (state.writing)
state.buffer.push(new WriteReq(chunk, encoding, cb));
else
doWrite(stream, state, len, chunk, encoding, cb);
return ret;
}
function doWrite(stream, state, len, chunk, encoding, cb) {
state.writelen = len;
state.writecb = cb;
state.writing = true;
state.sync = true;
stream._write(chunk, encoding, state.onwrite);
state.sync = false;
}
function onwriteError(stream, state, sync, er, cb) {
if (sync)
process.nextTick(function() {
cb(er);
});
else
cb(er);
stream._writableState.errorEmitted = true;
stream.emit('error', er);
}
function onwriteStateUpdate(state) {
state.writing = false;
state.writecb = null;
state.length -= state.writelen;
state.writelen = 0;
}
function onwrite(stream, er) {
var state = stream._writableState;
var sync = state.sync;
var cb = state.writecb;
onwriteStateUpdate(state);
if (er)
onwriteError(stream, state, sync, er, cb);
else {
// Check if we're actually ready to finish, but don't emit yet
var finished = needFinish(stream, state);
if (!finished && !state.bufferProcessing && state.buffer.length)
clearBuffer(stream, state);
if (sync) {
process.nextTick(function() {
afterWrite(stream, state, finished, cb);
});
} else {
afterWrite(stream, state, finished, cb);
}
}
}
function afterWrite(stream, state, finished, cb) {
if (!finished)
onwriteDrain(stream, state);
cb();
if (finished)
finishMaybe(stream, state);
}
// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
if (state.length === 0 && state.needDrain) {
state.needDrain = false;
stream.emit('drain');
}
}
// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
state.bufferProcessing = true;
for (var c = 0; c < state.buffer.length; c++) {
var entry = state.buffer[c];
var chunk = entry.chunk;
var encoding = entry.encoding;
var cb = entry.callback;
var len = state.objectMode ? 1 : chunk.length;
doWrite(stream, state, len, chunk, encoding, cb);
// if we didn't call the onwrite immediately, then
// it means that we need to wait until it does.
// also, that means that the chunk and cb are currently
// being processed, so move the buffer counter past them.
if (state.writing) {
c++;
break;
}
}
state.bufferProcessing = false;
if (c < state.buffer.length)
state.buffer = state.buffer.slice(c);
else
state.buffer.length = 0;
}
Writable.prototype._write = function(chunk, encoding, cb) {
cb(new Error('not implemented'));
};
Writable.prototype.end = function(chunk, encoding, cb) {
var state = this._writableState;
if (typeof chunk === 'function') {
cb = chunk;
chunk = null;
encoding = null;
} else if (typeof encoding === 'function') {
cb = encoding;
encoding = null;
}
if (typeof chunk !== 'undefined' && chunk !== null)
this.write(chunk, encoding);
// ignore unnecessary end() calls.
if (!state.ending && !state.finished)
endWritable(this, state, cb);
};
function needFinish(stream, state) {
return (state.ending &&
state.length === 0 &&
!state.finished &&
!state.writing);
}
function finishMaybe(stream, state) {
var need = needFinish(stream, state);
if (need) {
state.finished = true;
stream.emit('finish');
}
return need;
}
function endWritable(stream, state, cb) {
state.ending = true;
finishMaybe(stream, state);
if (cb) {
if (state.finished)
process.nextTick(cb);
else
stream.once('finish', cb);
}
state.ended = true;
}
@@ -1,71 +0,0 @@
{
"name": "readable-stream",
"version": "1.0.32",
"description": "Streams2, a user-land copy of the stream library from Node.js v0.10.x",
"main": "readable.js",
"dependencies": {
"core-util-is": "~1.0.0",
"isarray": "0.0.1",
"string_decoder": "~0.10.x",
"inherits": "~2.0.1"
},
"devDependencies": {
"tap": "~0.2.6"
},
"scripts": {
"test": "tap test/simple/*.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/readable-stream"
},
"keywords": [
"readable",
"stream",
"pipe"
],
"browser": {
"util": false
},
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "MIT",
"gitHead": "2024ad52b1e475465488b4ad39eb41d067ffcbb9",
"bugs": {
"url": "https://github.com/isaacs/readable-stream/issues"
},
"homepage": "https://github.com/isaacs/readable-stream",
"_id": "readable-stream@1.0.32",
"_shasum": "6b44a88ba984cd0ec0834ae7d59a47c39aef48ec",
"_from": "readable-stream@~1.0.26",
"_npmVersion": "2.0.2",
"_nodeVersion": "0.10.31",
"_npmUser": {
"name": "rvagg",
"email": "rod@vagg.org"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
},
{
"name": "tootallnate",
"email": "nathan@tootallnate.net"
},
{
"name": "rvagg",
"email": "rod@vagg.org"
}
],
"dist": {
"shasum": "6b44a88ba984cd0ec0834ae7d59a47c39aef48ec",
"tarball": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.32.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.32.tgz",
"readme": "ERROR: No README data found!"
}
@@ -1 +0,0 @@
module.exports = require("./lib/_stream_passthrough.js")
@@ -1,6 +0,0 @@
exports = module.exports = require('./lib/_stream_readable.js');
exports.Readable = exports;
exports.Writable = require('./lib/_stream_writable.js');
exports.Duplex = require('./lib/_stream_duplex.js');
exports.Transform = require('./lib/_stream_transform.js');
exports.PassThrough = require('./lib/_stream_passthrough.js');
@@ -1 +0,0 @@
module.exports = require("./lib/_stream_transform.js")
@@ -1 +0,0 @@
module.exports = require("./lib/_stream_writable.js")
-61
View File
@@ -1,61 +0,0 @@
{
"name": "bl",
"version": "0.9.3",
"description": "Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!",
"main": "bl.js",
"scripts": {
"test": "node test/test.js | faucet",
"test-local": "brtapsauce-local test/basic-test.js"
},
"repository": {
"type": "git",
"url": "https://github.com/rvagg/bl.git"
},
"homepage": "https://github.com/rvagg/bl",
"authors": [
"Rod Vagg <rod@vagg.org> (https://github.com/rvagg)",
"Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
"Jarett Cruger <jcrugzz@gmail.com> (https://github.com/jcrugzz)"
],
"keywords": [
"buffer",
"buffers",
"stream",
"awesomesauce"
],
"license": "MIT",
"dependencies": {
"readable-stream": "~1.0.26"
},
"devDependencies": {
"tape": "~2.12.3",
"hash_file": "~0.1.1",
"faucet": "~0.0.1",
"brtapsauce": "~0.3.0"
},
"gitHead": "4987a76bf6bafd7616e62c7023c955e62f3a9461",
"bugs": {
"url": "https://github.com/rvagg/bl/issues"
},
"_id": "bl@0.9.3",
"_shasum": "c41eff3e7cb31bde107c8f10076d274eff7f7d44",
"_from": "bl@0.9.3",
"_npmVersion": "1.4.27",
"_npmUser": {
"name": "rvagg",
"email": "rod@vagg.org"
},
"maintainers": [
{
"name": "rvagg",
"email": "rod@vagg.org"
}
],
"dist": {
"shasum": "c41eff3e7cb31bde107c8f10076d274eff7f7d44",
"tarball": "http://registry.npmjs.org/bl/-/bl-0.9.3.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz",
"readme": "ERROR: No README data found!"
}
-1
View File
@@ -1 +0,0 @@
module.exports = require('./lib');
-207
View File
@@ -1,207 +0,0 @@
// Load modules
var Http = require('http');
var NodeUtil = require('util');
var Hoek = require('hoek');
// Declare internals
var internals = {};
exports = module.exports = internals.Boom = function (/* (new Error) or (code, message) */) {
var self = this;
Hoek.assert(this.constructor === internals.Boom, 'Error must be instantiated using new');
Error.call(this);
this.isBoom = true;
this.response = {
code: 0,
payload: {},
headers: {}
// type: 'content-type'
};
if (arguments[0] instanceof Error) {
// Error
var error = arguments[0];
this.data = error;
this.response.code = error.code || 500;
if (error.message) {
this.message = error.message;
}
}
else {
// code, message
var code = arguments[0];
var message = arguments[1];
Hoek.assert(!isNaN(parseFloat(code)) && isFinite(code) && code >= 400, 'First argument must be a number (400+)');
this.response.code = code;
if (message) {
this.message = message;
}
}
// Response format
this.reformat();
return this;
};
NodeUtil.inherits(internals.Boom, Error);
internals.Boom.prototype.reformat = function () {
this.response.payload.code = this.response.code;
this.response.payload.error = Http.STATUS_CODES[this.response.code] || 'Unknown';
if (this.message) {
this.response.payload.message = Hoek.escapeHtml(this.message); // Prevent XSS from error message
}
};
// Utilities
internals.Boom.badRequest = function (message) {
return new internals.Boom(400, message);
};
internals.Boom.unauthorized = function (message, scheme, attributes) { // Or function (message, wwwAuthenticate[])
var err = new internals.Boom(401, message);
if (!scheme) {
return err;
}
var wwwAuthenticate = '';
if (typeof scheme === 'string') {
// function (message, scheme, attributes)
wwwAuthenticate = scheme;
if (attributes) {
var names = Object.keys(attributes);
for (var i = 0, il = names.length; i < il; ++i) {
if (i) {
wwwAuthenticate += ',';
}
var value = attributes[names[i]];
if (value === null ||
value === undefined) { // Value can be zero
value = '';
}
wwwAuthenticate += ' ' + names[i] + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"';
}
}
if (message) {
if (attributes) {
wwwAuthenticate += ',';
}
wwwAuthenticate += ' error="' + Hoek.escapeHeaderAttribute(message) + '"';
}
else {
err.isMissing = true;
}
}
else {
// function (message, wwwAuthenticate[])
var wwwArray = scheme;
for (var i = 0, il = wwwArray.length; i < il; ++i) {
if (i) {
wwwAuthenticate += ', ';
}
wwwAuthenticate += wwwArray[i];
}
}
err.response.headers['WWW-Authenticate'] = wwwAuthenticate;
return err;
};
internals.Boom.clientTimeout = function (message) {
return new internals.Boom(408, message);
};
internals.Boom.serverTimeout = function (message) {
return new internals.Boom(503, message);
};
internals.Boom.forbidden = function (message) {
return new internals.Boom(403, message);
};
internals.Boom.notFound = function (message) {
return new internals.Boom(404, message);
};
internals.Boom.internal = function (message, data) {
var err = new internals.Boom(500, message);
if (data && data.stack) {
err.trace = data.stack.split('\n');
err.outterTrace = Hoek.displayStack(1);
}
else {
err.trace = Hoek.displayStack(1);
}
err.data = data;
err.response.payload.message = 'An internal server error occurred'; // Hide actual error from user
return err;
};
internals.Boom.passThrough = function (code, payload, contentType, headers) {
var err = new internals.Boom(500, 'Pass-through'); // 500 code is only used to initialize
err.data = {
code: code,
payload: payload,
type: contentType
};
err.response.code = code;
err.response.type = contentType;
err.response.headers = headers;
err.response.payload = payload;
return err;
};
@@ -1,64 +0,0 @@
{
"name": "boom",
"description": "HTTP-friendly error objects",
"version": "0.4.2",
"author": {
"name": "Eran Hammer",
"email": "eran@hueniverse.com",
"url": "http://hueniverse.com"
},
"contributors": [],
"repository": {
"type": "git",
"url": "git://github.com/spumko/boom"
},
"main": "index",
"keywords": [
"error",
"http"
],
"engines": {
"node": ">=0.8.0"
},
"dependencies": {
"hoek": "0.9.x"
},
"devDependencies": {
"lab": "0.1.x",
"complexity-report": "0.x.x"
},
"scripts": {
"test": "make test-cov"
},
"licenses": [
{
"type": "BSD",
"url": "http://github.com/spumko/boom/raw/master/LICENSE"
}
],
"_id": "boom@0.4.2",
"dist": {
"shasum": "7a636e9ded4efcefb19cef4947a3c67dfaee911b",
"tarball": "http://registry.npmjs.org/boom/-/boom-0.4.2.tgz"
},
"_from": "boom@0.4.2",
"_npmVersion": "1.2.18",
"_npmUser": {
"name": "hueniverse",
"email": "eran@hueniverse.com"
},
"maintainers": [
{
"name": "hueniverse",
"email": "eran@hueniverse.com"
}
],
"directories": {},
"_shasum": "7a636e9ded4efcefb19cef4947a3c67dfaee911b",
"_resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz",
"bugs": {
"url": "https://github.com/spumko/boom/issues"
},
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/spumko/boom"
}
@@ -1,76 +0,0 @@
var lang = require('mout/lang');
var object = require('mout/object');
var rc = require('./util/rc');
var defaults = require('./util/defaults');
var expand = require('./util/expand');
var path = require('path');
function Config(cwd) {
this._cwd = cwd || process.cwd();
this._config = {};
}
Config.prototype.load = function () {
this._config = rc('bower', defaults, this._cwd);
return this;
};
Config.prototype.get = function (key) {
// TODO
};
Config.prototype.set = function (key, value) {
// TODO
return this;
};
Config.prototype.del = function (key, value) {
// TODO
return this;
};
Config.prototype.save = function (where, callback) {
// TODO
};
Config.prototype.toObject = function () {
var config = lang.deepClone(this._config);
config = Config.normalise(config);
return config;
};
Config.create = function (cwd) {
return new Config(cwd);
};
Config.read = function (cwd) {
var config = new Config(cwd);
return config.load().toObject();
};
Config.normalise = function (rawConfig) {
var config = {};
// Mix in defaults and raw config
object.deepMixIn(config, expand(defaults), expand(rawConfig));
// Some backwards compatible things..
config.shorthandResolver = config.shorthandResolver
.replace(/\{\{\{/g, '{{')
.replace(/\}\}\}/g, '}}');
// Ensure that every registry endpoint does not end with /
config.registry.search = config.registry.search.map(function (url) {
return url.replace(/\/+$/, '');
});
config.registry.register = config.registry.register.replace(/\/+$/, '');
config.registry.publish = config.registry.publish.replace(/\/+$/, '');
config.tmp = path.resolve(config.tmp);
return config;
};
Config.DEFAULT_REGISTRY = defaults.registry;
module.exports = Config;
@@ -1,44 +0,0 @@
var path = require('path');
var paths = require('./paths');
// Guess proxy defined in the env
/*jshint camelcase: false*/
var proxy = process.env.HTTP_PROXY
|| process.env.http_proxy
|| null;
var httpsProxy = process.env.HTTPS_PROXY
|| process.env.https_proxy
|| proxy;
/*jshint camelcase: true*/
// Use a well known user agent (in this case, curl) when using a proxy,
// to avoid potential filtering on many corporate proxies with blank or unknown agents
var userAgent = !proxy && !httpsProxy
? 'node/' + process.version + ' ' + process.platform + ' ' + process.arch
: 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5';
var defaults = {
'cwd': process.cwd(),
'directory': 'bower_components',
'registry': 'https://bower.herokuapp.com',
'shorthand-resolver': 'git://github.com/{{owner}}/{{package}}.git',
'tmp': paths.tmp,
'proxy': proxy,
'https-proxy': httpsProxy,
'timeout': 30000,
'ca': { search: [] },
'strict-ssl': true,
'user-agent': userAgent,
'color': true,
'interactive': null,
'storage': {
packages: path.join(paths.cache, 'packages'),
links: path.join(paths.data, 'links'),
completion: path.join(paths.data, 'completion'),
registry: path.join(paths.cache, 'registry'),
empty: path.join(paths.data, 'empty') // Empty dir, used in GIT_TEMPLATE_DIR among others
}
};
module.exports = defaults;
@@ -1,55 +0,0 @@
var object = require('mout/object');
var lang = require('mout/lang');
var string = require('mout/string');
function camelCase(config) {
var camelCased = {};
// Camel case
object.forOwn(config, function (value, key) {
// Ignore null values
if (value == null) {
return;
}
key = string.camelCase(key.replace(/_/g, '-'));
camelCased[key] = lang.isPlainObject(value) ? camelCase(value) : value;
});
return camelCased;
}
function expand(config) {
config = camelCase(config);
// Expand some properties
// Registry
if (typeof config.registry === 'string') {
config.registry = {
search: [config.registry],
register: config.registry,
publish: config.registry
};
} else if (config.registry) {
if (config.registry.search && !Array.isArray(config.registry.search)) {
config.registry.search = [config.registry.search];
}
}
// CA
if (typeof config.ca === 'string') {
config.ca = {
search: [config.ca],
register: config.ca,
publish: config.ca
};
} else if (config.ca) {
if (config.ca.search && !Array.isArray(config.ca.search)) {
config.ca.search = [config.ca.search];
}
}
return config;
}
module.exports = expand;
@@ -1,44 +0,0 @@
var os = require('os');
var path = require('path');
var osenv = require('osenv');
var crypto = require('crypto');
function generateFakeUser() {
var uid = process.pid + '-' + Date.now() + '-' + Math.floor(Math.random() * 1000000);
return crypto.createHash('md5').update(uid).digest('hex');
}
// Assume XDG defaults
// See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
var paths = {
config: process.env.XDG_CONFIG_HOME,
data: process.env.XDG_DATA_HOME,
cache: process.env.XDG_CACHE_HOME
};
// Guess some needed properties based on the user OS
var user = (osenv.user() || generateFakeUser()).replace(/\\/g, '-');
var tmp = path.join(os.tmpdir ? os.tmpdir() : os.tmpDir(), user);
var home = osenv.home();
var base;
// Fallbacks for windows
if (process.platform === 'win32') {
base = path.resolve(process.env.APPDATA || home || tmp);
base = path.join(base, 'bower');
paths.config = paths.config || path.join(base, 'config');
paths.data = paths.data || path.join(base, 'data');
paths.cache = paths.cache || path.join(base, 'cache');
// Fallbacks for other operating systems
} else {
base = path.resolve(home || tmp);
paths.config = paths.config || path.join(base, '.config/bower');
paths.data = paths.data || path.join(base, '.local/share/bower');
paths.cache = paths.cache || path.join(base, '.cache/bower');
}
paths.tmp = path.resolve(path.join(tmp, 'bower'));
module.exports = paths;
@@ -1,116 +0,0 @@
var path = require('path');
var fs = require('graceful-fs');
var optimist = require('optimist');
var osenv = require('osenv');
var object = require('mout/object');
var string = require('mout/string');
var paths = require('./paths');
var win = process.platform === 'win32';
var home = osenv.home();
function rc(name, defaults, cwd, argv) {
var argvConfig;
defaults = defaults || {};
cwd = cwd || process.cwd();
argv = argv || optimist.argv;
// Parse --config.foo=false
argvConfig = object.map(argv.config || {}, function (value) {
return value === 'false' ? false : value;
});
return object.deepMixIn.apply(null, [
{},
defaults,
{ cwd: cwd },
win ? {} : json(path.join('/etc', name + 'rc')),
!home ? {} : json(path.join(home, '.' + name + 'rc')),
json(path.join(paths.config, name + 'rc')),
json(find('.' + name + 'rc', cwd)),
env(name + '_'),
argvConfig
]);
}
function parse(content, file) {
var error;
if (!content.trim().length) {
return {};
}
try {
return JSON.parse(content);
} catch (e) {
if (file) {
error = new Error('Unable to parse ' + file + ': ' + e.message);
} else {
error = new Error('Unable to parse rc config: ' + e.message);
}
error.details = content;
error.code = 'EMALFORMED';
throw error;
}
return null;
}
function json(file) {
var content;
try {
content = fs.readFileSync(file).toString();
} catch (err) {
return null;
}
return parse(content, file);
}
function env(prefix) {
var obj = {};
var prefixLength = prefix.length;
prefix = prefix.toLowerCase();
object.forOwn(process.env, function (value, key) {
key = key.toLowerCase();
if (string.startsWith(key, prefix)) {
var parsedKey = key
.substr(prefixLength)
.replace(/__/g, '.') // __ is used for nesting
.replace(/_/g, '-'); // _ is used as a - separator
object.set(obj, parsedKey, value);
}
});
return obj;
}
function find(filename, dir) {
var walk = function (filename, dir) {
var file = path.join(dir, filename);
var parent = path.dirname(dir);
try {
fs.statSync(file);
return file;
} catch (err) {
// Check if we hit the root
if (parent === dir) {
return null;
}
return walk(filename, parent);
}
};
dir = dir || process.cwd();
return walk(filename, dir);
}
module.exports = rc;
@@ -1,160 +0,0 @@
// Monkey-patching the fs module.
// It's ugly, but there is simply no other way to do this.
var fs = module.exports = require('fs')
var assert = require('assert')
// fix up some busted stuff, mostly on windows and old nodes
require('./polyfills.js')
// The EMFILE enqueuing stuff
var util = require('util')
function noop () {}
var debug = noop
if (util.debuglog)
debug = util.debuglog('gfs')
else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || ''))
debug = function() {
var m = util.format.apply(util, arguments)
m = 'GFS: ' + m.split(/\n/).join('\nGFS: ')
console.error(m)
}
if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) {
process.on('exit', function() {
debug('fds', fds)
debug(queue)
assert.equal(queue.length, 0)
})
}
var originalOpen = fs.open
fs.open = open
function open(path, flags, mode, cb) {
if (typeof mode === "function") cb = mode, mode = null
if (typeof cb !== "function") cb = noop
new OpenReq(path, flags, mode, cb)
}
function OpenReq(path, flags, mode, cb) {
this.path = path
this.flags = flags
this.mode = mode
this.cb = cb
Req.call(this)
}
util.inherits(OpenReq, Req)
OpenReq.prototype.process = function() {
originalOpen.call(fs, this.path, this.flags, this.mode, this.done)
}
var fds = {}
OpenReq.prototype.done = function(er, fd) {
debug('open done', er, fd)
if (fd)
fds['fd' + fd] = this.path
Req.prototype.done.call(this, er, fd)
}
var originalReaddir = fs.readdir
fs.readdir = readdir
function readdir(path, cb) {
if (typeof cb !== "function") cb = noop
new ReaddirReq(path, cb)
}
function ReaddirReq(path, cb) {
this.path = path
this.cb = cb
Req.call(this)
}
util.inherits(ReaddirReq, Req)
ReaddirReq.prototype.process = function() {
originalReaddir.call(fs, this.path, this.done)
}
ReaddirReq.prototype.done = function(er, files) {
if (files && files.sort)
files = files.sort()
Req.prototype.done.call(this, er, files)
onclose()
}
var originalClose = fs.close
fs.close = close
function close (fd, cb) {
debug('close', fd)
if (typeof cb !== "function") cb = noop
delete fds['fd' + fd]
originalClose.call(fs, fd, function(er) {
onclose()
cb(er)
})
}
var originalCloseSync = fs.closeSync
fs.closeSync = closeSync
function closeSync (fd) {
try {
return originalCloseSync(fd)
} finally {
onclose()
}
}
// Req class
function Req () {
// start processing
this.done = this.done.bind(this)
this.failures = 0
this.process()
}
Req.prototype.done = function (er, result) {
var tryAgain = false
if (er) {
var code = er.code
var tryAgain = code === "EMFILE"
if (process.platform === "win32")
tryAgain = tryAgain || code === "OK"
}
if (tryAgain) {
this.failures ++
enqueue(this)
} else {
var cb = this.cb
cb(er, result)
}
}
var queue = []
function enqueue(req) {
queue.push(req)
debug('enqueue %d %s', queue.length, req.constructor.name, req)
}
function onclose() {
var req = queue.shift()
if (req) {
debug('process', req.constructor.name, req)
req.process()
}
}
@@ -1,65 +0,0 @@
{
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me"
},
"name": "graceful-fs",
"description": "A drop-in replacement for fs, making various improvements.",
"version": "2.0.3",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-graceful-fs.git"
},
"main": "graceful-fs.js",
"engines": {
"node": ">=0.4.0"
},
"directories": {
"test": "test"
},
"scripts": {
"test": "tap test/*.js"
},
"keywords": [
"fs",
"module",
"reading",
"retry",
"retries",
"queue",
"error",
"errors",
"handling",
"EMFILE",
"EAGAIN",
"EINVAL",
"EPERM",
"EACCESS"
],
"license": "BSD",
"bugs": {
"url": "https://github.com/isaacs/node-graceful-fs/issues"
},
"homepage": "https://github.com/isaacs/node-graceful-fs",
"_id": "graceful-fs@2.0.3",
"dist": {
"shasum": "7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0",
"tarball": "http://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz"
},
"_from": "graceful-fs@~2.0.0",
"_npmVersion": "1.4.6",
"_npmUser": {
"name": "isaacs",
"email": "i@izs.me"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"_shasum": "7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0",
"_resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz",
"readme": "ERROR: No README data found!"
}
@@ -1,228 +0,0 @@
var fs = require('fs')
var constants = require('constants')
var origCwd = process.cwd
var cwd = null
process.cwd = function() {
if (!cwd)
cwd = origCwd.call(process)
return cwd
}
var chdir = process.chdir
process.chdir = function(d) {
cwd = null
chdir.call(process, d)
}
// (re-)implement some things that are known busted or missing.
// lchmod, broken prior to 0.6.2
// back-port the fix here.
if (constants.hasOwnProperty('O_SYMLINK') &&
process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
fs.lchmod = function (path, mode, callback) {
callback = callback || noop
fs.open( path
, constants.O_WRONLY | constants.O_SYMLINK
, mode
, function (err, fd) {
if (err) {
callback(err)
return
}
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchmod(fd, mode, function (err) {
fs.close(fd, function(err2) {
callback(err || err2)
})
})
})
}
fs.lchmodSync = function (path, mode) {
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
var err, err2
try {
var ret = fs.fchmodSync(fd, mode)
} catch (er) {
err = er
}
try {
fs.closeSync(fd)
} catch (er) {
err2 = er
}
if (err || err2) throw (err || err2)
return ret
}
}
// lutimes implementation, or no-op
if (!fs.lutimes) {
if (constants.hasOwnProperty("O_SYMLINK")) {
fs.lutimes = function (path, at, mt, cb) {
fs.open(path, constants.O_SYMLINK, function (er, fd) {
cb = cb || noop
if (er) return cb(er)
fs.futimes(fd, at, mt, function (er) {
fs.close(fd, function (er2) {
return cb(er || er2)
})
})
})
}
fs.lutimesSync = function (path, at, mt) {
var fd = fs.openSync(path, constants.O_SYMLINK)
, err
, err2
, ret
try {
var ret = fs.futimesSync(fd, at, mt)
} catch (er) {
err = er
}
try {
fs.closeSync(fd)
} catch (er) {
err2 = er
}
if (err || err2) throw (err || err2)
return ret
}
} else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
// maybe utimensat will be bound soonish?
fs.lutimes = function (path, at, mt, cb) {
fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
}
fs.lutimesSync = function (path, at, mt) {
return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
}
} else {
fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
fs.lutimesSync = function () {}
}
}
// https://github.com/isaacs/node-graceful-fs/issues/4
// Chown should not fail on einval or eperm if non-root.
fs.chown = chownFix(fs.chown)
fs.fchown = chownFix(fs.fchown)
fs.lchown = chownFix(fs.lchown)
fs.chownSync = chownFixSync(fs.chownSync)
fs.fchownSync = chownFixSync(fs.fchownSync)
fs.lchownSync = chownFixSync(fs.lchownSync)
function chownFix (orig) {
if (!orig) return orig
return function (target, uid, gid, cb) {
return orig.call(fs, target, uid, gid, function (er, res) {
if (chownErOk(er)) er = null
cb(er, res)
})
}
}
function chownFixSync (orig) {
if (!orig) return orig
return function (target, uid, gid) {
try {
return orig.call(fs, target, uid, gid)
} catch (er) {
if (!chownErOk(er)) throw er
}
}
}
function chownErOk (er) {
// if there's no getuid, or if getuid() is something other than 0,
// and the error is EINVAL or EPERM, then just ignore it.
// This specific case is a silent failure in cp, install, tar,
// and most other unix tools that manage permissions.
// When running as root, or if other types of errors are encountered,
// then it's strict.
if (!er || (!process.getuid || process.getuid() !== 0)
&& (er.code === "EINVAL" || er.code === "EPERM")) return true
}
// if lchmod/lchown do not exist, then make them no-ops
if (!fs.lchmod) {
fs.lchmod = function (path, mode, cb) {
process.nextTick(cb)
}
fs.lchmodSync = function () {}
}
if (!fs.lchown) {
fs.lchown = function (path, uid, gid, cb) {
process.nextTick(cb)
}
fs.lchownSync = function () {}
}
// on Windows, A/V software can lock the directory, causing this
// to fail with an EACCES or EPERM if the directory contains newly
// created files. Try again on failure, for up to 1 second.
if (process.platform === "win32") {
var rename_ = fs.rename
fs.rename = function rename (from, to, cb) {
var start = Date.now()
rename_(from, to, function CB (er) {
if (er
&& (er.code === "EACCES" || er.code === "EPERM")
&& Date.now() - start < 1000) {
return rename_(from, to, CB)
}
cb(er)
})
}
}
// if read() returns EAGAIN, then just try it again.
var read = fs.read
fs.read = function (fd, buffer, offset, length, position, callback_) {
var callback
if (callback_ && typeof callback_ === 'function') {
var eagCounter = 0
callback = function (er, _, __) {
if (er && er.code === 'EAGAIN' && eagCounter < 10) {
eagCounter ++
return read.call(fs, fd, buffer, offset, length, position, callback)
}
callback_.apply(this, arguments)
}
}
return read.call(fs, fd, buffer, offset, length, position, callback)
}
var readSync = fs.readSync
fs.readSync = function (fd, buffer, offset, length, position) {
var eagCounter = 0
while (true) {
try {
return readSync.call(fs, fd, buffer, offset, length, position)
} catch (er) {
if (er.code === 'EAGAIN' && eagCounter < 10) {
eagCounter ++
continue
}
throw er
}
}
}
@@ -1,50 +0,0 @@
//automatically generated, do not edit!
//run `node build` instead
module.exports = {
'append' : require('./array/append'),
'collect' : require('./array/collect'),
'combine' : require('./array/combine'),
'compact' : require('./array/compact'),
'contains' : require('./array/contains'),
'difference' : require('./array/difference'),
'every' : require('./array/every'),
'filter' : require('./array/filter'),
'find' : require('./array/find'),
'findIndex' : require('./array/findIndex'),
'findLast' : require('./array/findLast'),
'findLastIndex' : require('./array/findLastIndex'),
'flatten' : require('./array/flatten'),
'forEach' : require('./array/forEach'),
'indexOf' : require('./array/indexOf'),
'insert' : require('./array/insert'),
'intersection' : require('./array/intersection'),
'invoke' : require('./array/invoke'),
'join' : require('./array/join'),
'lastIndexOf' : require('./array/lastIndexOf'),
'map' : require('./array/map'),
'max' : require('./array/max'),
'min' : require('./array/min'),
'pick' : require('./array/pick'),
'pluck' : require('./array/pluck'),
'range' : require('./array/range'),
'reduce' : require('./array/reduce'),
'reduceRight' : require('./array/reduceRight'),
'reject' : require('./array/reject'),
'remove' : require('./array/remove'),
'removeAll' : require('./array/removeAll'),
'shuffle' : require('./array/shuffle'),
'slice' : require('./array/slice'),
'some' : require('./array/some'),
'sort' : require('./array/sort'),
'sortBy' : require('./array/sortBy'),
'split' : require('./array/split'),
'toLookup' : require('./array/toLookup'),
'union' : require('./array/union'),
'unique' : require('./array/unique'),
'xor' : require('./array/xor'),
'zip' : require('./array/zip')
};
@@ -1,21 +0,0 @@
/**
* Appends an array to the end of another.
* The first array will be modified.
*/
function append(arr1, arr2) {
if (arr2 == null) {
return arr1;
}
var pad = arr1.length,
i = -1,
len = arr2.length;
while (++i < len) {
arr1[pad + i] = arr2[i];
}
return arr1;
}
module.exports = append;
@@ -1,27 +0,0 @@
var append = require('./append');
var makeIterator = require('../function/makeIterator_');
/**
* Maps the items in the array and concatenates the result arrays.
*/
function collect(arr, callback, thisObj){
callback = makeIterator(callback, thisObj);
var results = [];
if (arr == null) {
return results;
}
var i = -1, len = arr.length;
while (++i < len) {
var value = callback(arr[i], i, arr);
if (value != null) {
append(results, value);
}
}
return results;
}
module.exports = collect;
@@ -1,22 +0,0 @@
var indexOf = require('./indexOf');
/**
* Combines an array with all the items of another.
* Does not allow duplicates and is case and type sensitive.
*/
function combine(arr1, arr2) {
if (arr2 == null) {
return arr1;
}
var i = -1, len = arr2.length;
while (++i < len) {
if (indexOf(arr1, arr2[i]) === -1) {
arr1.push(arr2[i]);
}
}
return arr1;
}
module.exports = combine;
@@ -1,13 +0,0 @@
var filter = require('./filter');
/**
* Remove all null/undefined items from array.
*/
function compact(arr) {
return filter(arr, function(val){
return (val != null);
});
}
module.exports = compact;
@@ -1,10 +0,0 @@
var indexOf = require('./indexOf');
/**
* If array contains values.
*/
function contains(arr, val) {
return indexOf(arr, val) !== -1;
}
module.exports = contains;
@@ -1,23 +0,0 @@
var unique = require('./unique');
var filter = require('./filter');
var some = require('./some');
var contains = require('./contains');
var slice = require('./slice');
/**
* Return a new Array with elements that aren't present in the other Arrays.
*/
function difference(arr) {
var arrs = slice(arguments, 1),
result = filter(unique(arr), function(needle){
return !some(arrs, function(haystack){
return contains(haystack, needle);
});
});
return result;
}
module.exports = difference;
@@ -1,27 +0,0 @@
var makeIterator = require('../function/makeIterator_');
/**
* Array every
*/
function every(arr, callback, thisObj) {
callback = makeIterator(callback, thisObj);
var result = true;
if (arr == null) {
return result;
}
var i = -1, len = arr.length;
while (++i < len) {
// we iterate over sparse items since there is no way to make it
// work properly on IE 7-8. see #64
if (!callback(arr[i], i, arr) ) {
result = false;
break;
}
}
return result;
}
module.exports = every;
@@ -1,26 +0,0 @@
var makeIterator = require('../function/makeIterator_');
/**
* Array filter
*/
function filter(arr, callback, thisObj) {
callback = makeIterator(callback, thisObj);
var results = [];
if (arr == null) {
return results;
}
var i = -1, len = arr.length, value;
while (++i < len) {
value = arr[i];
if (callback(value, i, arr)) {
results.push(value);
}
}
return results;
}
module.exports = filter;
@@ -1,13 +0,0 @@
var findIndex = require('./findIndex');
/**
* Returns first item that matches criteria
*/
function find(arr, iterator, thisObj){
var idx = findIndex(arr, iterator, thisObj);
return idx >= 0? arr[idx] : void(0);
}
module.exports = find;
@@ -1,23 +0,0 @@
var makeIterator = require('../function/makeIterator_');
/**
* Returns the index of the first item that matches criteria
*/
function findIndex(arr, iterator, thisObj){
iterator = makeIterator(iterator, thisObj);
if (arr == null) {
return -1;
}
var i = -1, len = arr.length;
while (++i < len) {
if (iterator(arr[i], i, arr)) {
return i;
}
}
return -1;
}
module.exports = findIndex;
@@ -1,13 +0,0 @@
var findLastIndex = require('./findLastIndex');
/**
* Returns last item that matches criteria
*/
function findLast(arr, iterator, thisObj){
var idx = findLastIndex(arr, iterator, thisObj);
return idx >= 0? arr[idx] : void(0);
}
module.exports = findLast;
@@ -1,24 +0,0 @@
var makeIterator = require('../function/makeIterator_');
/**
* Returns the index of the last item that matches criteria
*/
function findLastIndex(arr, iterator, thisObj){
iterator = makeIterator(iterator, thisObj);
if (arr == null) {
return -1;
}
var n = arr.length;
while (n-- >= 0) {
if (iterator(arr[n], n, arr)) {
return n;
}
}
return -1;
}
module.exports = findLastIndex;
@@ -1,43 +0,0 @@
var isArray = require('../lang/isArray');
var append = require('./append');
/*
* Helper function to flatten to a destination array.
* Used to remove the need to create intermediate arrays while flattening.
*/
function flattenTo(arr, result, level) {
if (arr == null) {
return result;
} else if (level === 0) {
append(result, arr);
return result;
}
var value,
i = -1,
len = arr.length;
while (++i < len) {
value = arr[i];
if (isArray(value)) {
flattenTo(value, result, level - 1);
} else {
result.push(value);
}
}
return result;
}
/**
* Recursively flattens an array.
* A new array containing all the elements is returned.
* If `shallow` is true, it will only flatten one level.
*/
function flatten(arr, level) {
level = level == null? -1 : level;
return flattenTo(arr, [], level);
}
module.exports = flatten;
@@ -1,23 +0,0 @@
/**
* Array forEach
*/
function forEach(arr, callback, thisObj) {
if (arr == null) {
return;
}
var i = -1,
len = arr.length;
while (++i < len) {
// we iterate over sparse items since there is no way to make it
// work properly on IE 7-8. see #64
if ( callback.call(thisObj, arr[i], i, arr) === false ) {
break;
}
}
}
module.exports = forEach;

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