diff options
Diffstat (limited to 'deps/npm/lib')
26 files changed, 465 insertions, 260 deletions
diff --git a/deps/npm/lib/cache/add-local.js b/deps/npm/lib/cache/add-local.js index 075ca5fc61..0794b92144 100644 --- a/deps/npm/lib/cache/add-local.js +++ b/deps/npm/lib/cache/add-local.js @@ -20,7 +20,7 @@ module.exports = addLocal function addLocal (p, pkgData, cb_) { assert(typeof p === 'object', 'must have spec info') - assert(typeof cb === 'function', 'must have callback') + assert(typeof cb_ === 'function', 'must have callback') pkgData = pkgData || {} @@ -91,10 +91,10 @@ function addLocalDirectory (p, pkgData, shasum, cb) { getCacheStat(function (er, cs) { mkdir(path.dirname(pj), function (er, made) { - if (er) return cb(er) + if (er) return wrapped(er) var doPrePublish = !pathIsInside(p, npm.tmp) if (doPrePublish) { - lifecycle(data, 'prepublish', p, iferr(cb, thenPack)) + lifecycle(data, 'prepublish', p, iferr(wrapped, thenPack)) } else { thenPack() } @@ -102,12 +102,15 @@ function addLocalDirectory (p, pkgData, shasum, cb) { tar.pack(tgz, p, data, function (er) { if (er) { log.error('addLocalDirectory', 'Could not pack', p, 'to', tgz) - return cb(er) + return wrapped(er) } - if (!cs || isNaN(cs.uid) || isNaN(cs.gid)) wrapped() + if (!cs || isNaN(cs.uid) || isNaN(cs.gid)) return wrapped() - chownr(made || tgz, cs.uid, cs.gid, wrapped) + chownr(made || tgz, cs.uid, cs.gid, function (er) { + if (er && er.code === 'ENOENT') return wrapped() + wrapped(er) + }) }) } }) diff --git a/deps/npm/lib/cache/add-remote-git.js b/deps/npm/lib/cache/add-remote-git.js index d8f64a85be..0da1554f80 100644 --- a/deps/npm/lib/cache/add-remote-git.js +++ b/deps/npm/lib/cache/add-remote-git.js @@ -26,6 +26,7 @@ var templates = path.join(remotes, '_templates') var VALID_VARIABLES = [ 'GIT_ASKPASS', + 'GIT_EXEC_PATH', 'GIT_PROXY_COMMAND', 'GIT_SSH', 'GIT_SSH_COMMAND', diff --git a/deps/npm/lib/config.js b/deps/npm/lib/config.js index 7112e88c5f..0426546274 100644 --- a/deps/npm/lib/config.js +++ b/deps/npm/lib/config.js @@ -12,6 +12,7 @@ var os = require('os') var umask = require('./utils/umask') var usage = require('./utils/usage') var output = require('./utils/output') +var noProgressTillDone = require('./utils/no-progress-while-running').tillDone config.usage = usage( 'config', @@ -106,7 +107,7 @@ function edit (cb) { data, function (er) { if (er) return cb(er) - editor(f, { editor: e }, cb) + editor(f, { editor: e }, noProgressTillDone(cb)) } ) }) diff --git a/deps/npm/lib/config/cmd-list.js b/deps/npm/lib/config/cmd-list.js index 34ebeed448..d66a5722db 100644 --- a/deps/npm/lib/config/cmd-list.js +++ b/deps/npm/lib/config/cmd-list.js @@ -66,6 +66,7 @@ var cmdList = [ 'stars', 'tag', 'adduser', + 'login', // This is an alias for `adduser` but it can be confusing 'logout', 'unpublish', 'owner', diff --git a/deps/npm/lib/edit.js b/deps/npm/lib/edit.js index 155db6fd97..8e9bbd1797 100644 --- a/deps/npm/lib/edit.js +++ b/deps/npm/lib/edit.js @@ -10,6 +10,7 @@ var npm = require('./npm.js') var path = require('path') var fs = require('graceful-fs') var editor = require('editor') +var noProgressTillDone = require('./utils/no-progress-while-running').tillDone function edit (args, cb) { var p = args[0] @@ -26,9 +27,9 @@ function edit (args, cb) { var f = path.resolve(npm.dir, p) fs.lstat(f, function (er) { if (er) return cb(er) - editor(f, { editor: e }, function (er) { + editor(f, { editor: e }, noProgressTillDone(function (er) { if (er) return cb(er) npm.commands.rebuild(args, cb) - }) + })) }) } diff --git a/deps/npm/lib/fetch-package-metadata.js b/deps/npm/lib/fetch-package-metadata.js index d4f02bb278..85322304d5 100644 --- a/deps/npm/lib/fetch-package-metadata.js +++ b/deps/npm/lib/fetch-package-metadata.js @@ -122,7 +122,15 @@ function fetchNamedPackageData (dep, next) { } function pickVersionFromRegistryDocument (pkg) { if (!regCache[url]) regCache[url] = pkg - var versions = Object.keys(pkg.versions).sort(semver.rcompare) + var versions = Object.keys(pkg.versions) + + var invalidVersions = versions.filter(function (v) { return !semver.valid(v) }) + if (invalidVersions.length > 0) { + log.warn('pickVersion', 'The package %s has invalid semver-version(s): %s. This usually only happens for unofficial private registries. ' + + 'You should delete or re-publish the invalid versions.', pkg.name, invalidVersions.join(', ')) + } + + versions = versions.filter(function (v) { return semver.valid(v) }).sort(semver.rcompare) if (dep.type === 'tag') { var tagVersion = pkg['dist-tags'][dep.spec] @@ -169,6 +177,7 @@ function fetchNamedPackageData (dep, next) { : 'No valid targets found.' var er = new Error('No compatible version found: ' + dep.raw + '\n' + targets) + er.code = 'ETARGET' return next(er) } })) diff --git a/deps/npm/lib/init.js b/deps/npm/lib/init.js index a5d102c1f3..df5615e476 100644 --- a/deps/npm/lib/init.js +++ b/deps/npm/lib/init.js @@ -6,13 +6,13 @@ var log = require('npmlog') var npm = require('./npm.js') var initJson = require('init-package-json') var output = require('./utils/output.js') +var noProgressTillDone = require('./utils/no-progress-while-running').tillDone init.usage = 'npm init [--force|-f|--yes|-y]' function init (args, cb) { var dir = process.cwd() log.pause() - log.disableProgress() var initFile = npm.config.get('init-module') if (!initJson.yes(npm.config)) { output([ @@ -28,7 +28,7 @@ function init (args, cb) { 'Press ^C at any time to quit.' ].join('\n')) } - initJson(dir, initFile, npm.config, function (er, data) { + initJson(dir, initFile, npm.config, noProgressTillDone(function (er, data) { log.resume() log.silly('package data', data) if (er && er.message === 'canceled') { @@ -37,5 +37,5 @@ function init (args, cb) { } log.info('init', 'written successfully') cb(er, data) - }) + })) } diff --git a/deps/npm/lib/install.js b/deps/npm/lib/install.js index 1fb5a4f4e6..4c3d0def78 100644 --- a/deps/npm/lib/install.js +++ b/deps/npm/lib/install.js @@ -1,8 +1,8 @@ 'use strict' // npm install <pkg> <pkg> <pkg> // -// See doc/install.md for more description - +// See doc/cli/npm-install.md for more description +// // Managing contexts... // there's a lot of state associated with an "install" operation, including // packages that are already installed, parent packages, current shrinkwrap, and @@ -259,6 +259,9 @@ Installer.prototype.run = function (cb) { [this, this.debugActions, 'decomposeActions', 'todo']) if (!this.dryrun) { installSteps.push( + [this.newTracker(log, 'runTopLevelLifecycles', 2)], + [this, this.runPreinstallTopLevelLifecycles], + [this.newTracker(log, 'executeActions', 8)], [this, this.executeActions], [this, this.finishTracker, 'executeActions']) @@ -269,10 +272,9 @@ Installer.prototype.run = function (cb) { [this, this.rollbackFailedOptional, staging, this.todo], [this, this.finishTracker, 'rollbackFailedOptional'], [this, this.commit, staging, this.todo], - [this.newTracker(log, 'runTopLevelLifecycles', 2)], - [this, this.runTopLevelLifecycles], - [this, this.finishTracker, 'runTopLevelLifecycles']) + [this, this.runPostinstallTopLevelLifecycles], + [this, this.finishTracker, 'runTopLevelLifecycles']) if (getSaveType(this.args)) { postInstallSteps.push( [this, this.saveToDependencies]) @@ -289,9 +291,13 @@ Installer.prototype.run = function (cb) { self.idealTree.warnings.forEach(function (warning) { if (warning.code === 'EPACKAGEJSON' && self.global) return if (warning.code === 'ENOTDIR') return - errorMessage(warning).summary.forEach(function (logline) { + var output = errorMessage(warning) + output.summary.forEach(function (logline) { log.warn.apply(log, logline) }) + output.detail.forEach(function (logline) { + log.verbose.apply(log, logline) + }) }) } if (installEr && postInstallEr) { @@ -310,7 +316,7 @@ Installer.prototype.run = function (cb) { Installer.prototype.loadArgMetadata = function (next) { var self = this - getAllMetadata(this.args, this.currentTree, iferr(next, function (args) { + getAllMetadata(this.args, this.currentTree, process.cwd(), iferr(next, function (args) { self.args = args next() })) @@ -520,19 +526,29 @@ Installer.prototype.commit = function (staging, actionsToRun, cb) { }, cb) } -Installer.prototype.runTopLevelLifecycles = function (cb) { +Installer.prototype.runPreinstallTopLevelLifecycles = function (cb) { validate('F', arguments) if (this.failing) return cb() - log.silly('install', 'runTopLevelLifecycles') + if (!this.topLevelLifecycles) return cb() + log.silly('install', 'runPreinstallTopLevelLifecycles') + var steps = [] + var trackLifecycle = this.progress.runTopLevelLifecycles + + steps.push( + [doOneAction, 'preinstall', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('preinstall:.')] + ) + chain(steps, cb) +} + +Installer.prototype.runPostinstallTopLevelLifecycles = function (cb) { + validate('F', arguments) + if (this.failing) return cb() + if (!this.topLevelLifecycles) return cb() + log.silly('install', 'runPostinstallTopLevelLifecycles') var steps = [] var trackLifecycle = this.progress.runTopLevelLifecycles - if (!this.topLevelLifecycles) { - trackLifecycle.finish() - return cb() - } steps.push( - [doOneAction, 'preinstall', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('preinstall:.')], [doOneAction, 'build', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('build:.')], [doOneAction, 'install', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('install:.')], [doOneAction, 'postinstall', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('postinstall:.')]) @@ -591,18 +607,19 @@ Installer.prototype.readLocalPackageData = function (cb) { return cb(er) } if (!currentTree.package) currentTree.package = {} - self.loadArgMetadata(iferr(cb, function () { - if (currentTree.package._shrinkwrap) return cb() + if (currentTree.package._shrinkwrap) { + self.loadArgMetadata(cb) + } else { fs.readFile(path.join(self.where, 'npm-shrinkwrap.json'), function (er, data) { - if (er) return cb() + if (er) return self.loadArgMetadata(cb) try { currentTree.package._shrinkwrap = parseJSON(data) } catch (ex) { return cb(ex) } - return cb() + return self.loadArgMetadata(cb) }) - })) + } })) })) } diff --git a/deps/npm/lib/install/action/update-linked.js b/deps/npm/lib/install/action/update-linked.js index 46c4cdfadd..b37f477e17 100644 --- a/deps/npm/lib/install/action/update-linked.js +++ b/deps/npm/lib/install/action/update-linked.js @@ -2,7 +2,9 @@ var path = require('path') module.exports = function (top, buildpath, pkg, log, next) { - log.warn('update-linked', path.relative(top, pkg.path), 'needs updating to', pkg.package.version, - 'from', pkg.oldPkg.package.version, "but we can't, as it's a symlink") + if (pkg.package.version !== pkg.oldPkg.package.version) { + log.warn('update-linked', path.relative(top, pkg.path), 'needs updating to', pkg.package.version, + 'from', pkg.oldPkg.package.version, "but we can't, as it's a symlink") + } next() } diff --git a/deps/npm/lib/install/actions.js b/deps/npm/lib/install/actions.js index 1c7462030c..7c3f97cefc 100644 --- a/deps/npm/lib/install/actions.js +++ b/deps/npm/lib/install/actions.js @@ -3,13 +3,14 @@ var path = require('path') var validate = require('aproba') var chain = require('slide').chain var asyncMap = require('slide').asyncMap -var log = require('npmlog') +var iferr = require('iferr') var andFinishTracker = require('./and-finish-tracker.js') var andAddParentToErrors = require('./and-add-parent-to-errors.js') var failedDependency = require('./deps.js').failedDependency -var packageId = require('../utils/package-id.js') var moduleName = require('../utils/module-name.js') var buildPath = require('./build-path.js') +var reportOptionalFailure = require('./report-optional-failure.js') +var isInstallable = require('./validate-args.js').isInstallable var actions = {} @@ -45,7 +46,23 @@ Object.keys(actions).forEach(function (actionName) { if (!pkg.commit) pkg.commit = [] pkg.commit.push(action.commit) } - return action(top, buildpath, pkg, log, andFinishTracker(log, andAddParentToErrors(pkg.parent, andHandleOptionalDepErrors(pkg, next)))) + if (pkg.knownInstallable) { + return thenRunAction() + } else { + return isInstallable(pkg.package, iferr(andDone(next), andMarkInstallable(thenRunAction))) + } + function andMarkInstallable (cb) { + return function () { + pkg.knownInstallable = true + cb() + } + } + function thenRunAction () { + action(top, buildpath, pkg, log, andDone(next)) + } + function andDone (cb) { + return andFinishTracker(log, andAddParentToErrors(pkg.parent, andHandleOptionalDepErrors(pkg, cb))) + } } }) @@ -70,8 +87,7 @@ function andHandleOptionalDepErrors (pkg, next) { if (isFatal) anyFatal = true } if (anyFatal) return next.apply(null, arguments) - log.warn('install:' + packageId(pkg), er.message) - log.verbose('install:' + packageId(pkg), er.stack) + reportOptionalFailure(pkg, null, er) next() } } diff --git a/deps/npm/lib/install/deps.js b/deps/npm/lib/install/deps.js index 886e841913..762f396752 100644 --- a/deps/npm/lib/install/deps.js +++ b/deps/npm/lib/install/deps.js @@ -1,7 +1,6 @@ 'use strict' var assert = require('assert') var path = require('path') -var url = require('url') var semver = require('semver') var asyncMap = require('slide').asyncMap var chain = require('slide').chain @@ -10,6 +9,7 @@ var iferr = require('iferr') var npa = require('npm-package-arg') var validate = require('aproba') var realizePackageSpecifier = require('realize-package-specifier') +var realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier') var asap = require('asap') var dezalgo = require('dezalgo') var fetchPackageMetadata = require('../fetch-package-metadata.js') @@ -21,13 +21,16 @@ var inflateShrinkwrap = require('./inflate-shrinkwrap.js') var inflateBundled = require('./inflate-bundled.js') var andFinishTracker = require('./and-finish-tracker.js') var npm = require('../npm.js') -var flatName = require('./flatten-tree.js').flatName +var flatNameFromTree = require('./flatten-tree.js').flatNameFromTree var createChild = require('./node.js').create var resetMetadata = require('./node.js').reset var andIgnoreErrors = require('./and-ignore-errors.js') var isInstallable = require('./validate-args.js').isInstallable var packageId = require('../utils/package-id.js') var moduleName = require('../utils/module-name.js') +var isDevDep = require('./is-dev-dep.js') +var isProdDep = require('./is-prod-dep.js') +var reportOptionalFailure = require('./report-optional-failure.js') // The export functions in this module mutate a dependency tree, adding // items to them. @@ -50,16 +53,6 @@ function isDep (tree, child, cb) { }) } -function isDevDep (tree, name, cb) { - var devDeps = tree.package.devDependencies || {} - return devDeps[name] -} - -function isProdDep (tree, name, cb) { - var deps = tree.package.dependencies || {} - return deps[name] -} - var registryTypes = { range: true, version: true } function doesChildVersionMatch (child, requested, requestor) { @@ -212,26 +205,29 @@ function getShrinkwrap (tree, name) { return tree.package._shrinkwrap && tree.package._shrinkwrap.dependencies && tree.package._shrinkwrap.dependencies[name] } -exports.getAllMetadata = function (args, tree, next) { - asyncMap(args, function (spec, done) { - if (tree && spec.lastIndexOf('@') <= 0) { - var sw = getShrinkwrap(tree, spec) +exports.getAllMetadata = function (args, tree, where, next) { + asyncMap(args, function (arg, done) { + function fetchMetadataWithVersion () { + var version = matchingDep(tree, arg) + var spec = version == null ? arg : arg + '@' + version + return fetchPackageMetadata(spec, where, done) + } + if (tree && arg.lastIndexOf('@') <= 0) { + var sw = getShrinkwrap(tree, arg) if (sw) { - // FIXME: This is duplicated in inflate-shrinkwrap and should be factoed - // into a shared function - spec = sw.resolved - ? spec + '@' + sw.resolved - : (sw.from && url.parse(sw.from).protocol) - ? spec + '@' + sw.from - : spec + '@' + sw.version + return realizeShrinkwrapSpecifier(arg, sw, where, function (err, spec) { + if (err) { + return fetchMetadataWithVersion() + } else { + return fetchPackageMetadata(spec, where, done) + } + }) } else { - var version = matchingDep(tree, spec) - if (version != null) { - spec += '@' + version - } + return fetchMetadataWithVersion() } + } else { + return fetchPackageMetadata(arg, where, done) } - fetchPackageMetadata(spec, packageRelativePath(tree), done) }, next) } @@ -354,18 +350,6 @@ var failedDependency = exports.failedDependency = function (tree, name_pkg) { return false } -function top (tree) { - if (tree.parent) return top(tree.parent) - return tree -} - -function treeWarn (tree, what, error) { - var topTree = top(tree) - if (!topTree.warnings) topTree.warnings = [] - error.optional = flatNameFromTree(tree) + '/' + what - topTree.warnings.push(error) -} - function andHandleOptionalErrors (log, tree, name, done) { validate('OOSF', arguments) return function (er, child, childLog) { @@ -374,7 +358,7 @@ function andHandleOptionalErrors (log, tree, name, done) { var isFatal = failedDependency(tree, name) if (er && !isFatal) { tree.children = tree.children.filter(noModuleNameMatches(name)) - treeWarn(tree, name, er) + reportOptionalFailure(tree, name, er) return done() } else { return done(er, child, childLog) @@ -449,7 +433,9 @@ function addDependency (name, versionSpec, tree, log, done) { } })) } else { - resolveWithNewModule(req, tree, log, next) + fetchPackageMetadata(req, packageRelativePath(tree), log.newItem('fetchMetadata'), iferr(next, function (pkg) { + resolveWithNewModule(pkg, tree, log, next) + })) } })) } @@ -471,14 +457,6 @@ var updatePhantomChildren = exports.updatePhantomChildren = function (current, c } } -function flatNameFromTree (tree) { - validate('O', arguments) - if (tree.isTop) return '/' - var path = flatNameFromTree(tree.parent) - if (path !== '/') path += '/' - return flatName(path, tree) -} - exports._replaceModuleByPath = replaceModuleByPath function replaceModuleByPath (obj, key, child) { return replaceModule(obj, key, child, function (replacing, child) { @@ -511,56 +489,47 @@ function replaceModule (obj, key, child, matchBy) { function resolveWithNewModule (pkg, tree, log, next) { validate('OOOF', arguments) - if (pkg.type) { - return fetchPackageMetadata(pkg, packageRelativePath(tree), log.newItem('fetchMetadata'), iferr(next, function (pkg) { - resolveWithNewModule(pkg, tree, log, next) - })) - } - - if (!pkg._installable) { - log.silly('resolveWithNewModule', packageId(pkg), 'checking installable status') - return isInstallable(pkg, iferr(next, function () { - pkg._installable = true - resolveWithNewModule(pkg, tree, log, next) - })) - } - - if (!pkg._from) { - pkg._from = pkg._requested.name + '@' + pkg._requested.spec - } - addShrinkwrap(pkg, iferr(next, function () { - addBundled(pkg, iferr(next, function () { - var parent = earliestInstallable(tree, tree, pkg) || tree - var child = createChild({ - package: pkg, - parent: parent, - path: path.join(parent.path, 'node_modules', pkg.name), - realpath: path.resolve(parent.realpath, 'node_modules', pkg.name), - children: pkg._bundled || [], - isLink: tree.isLink - }) - delete pkg._bundled - var hasBundled = child.children.length - - var replaced = replaceModuleByName(parent, 'children', child) - if (replaced) removeObsoleteDep(replaced) - addRequiredDep(tree, child, function () { - child.location = flatNameFromTree(child) - if (tree.parent && parent !== tree) updatePhantomChildren(tree.parent, child) - - if (hasBundled) { - inflateBundled(child, child.children) - } - - if (pkg._shrinkwrap && pkg._shrinkwrap.dependencies) { - return inflateShrinkwrap(child, pkg._shrinkwrap.dependencies, function (er) { - next(er, child, log) - }) - } + log.silly('resolveWithNewModule', packageId(pkg), 'checking installable status') + return isInstallable(pkg, iferr(next, function () { + if (!pkg._from) { + pkg._from = pkg._requested.name + '@' + pkg._requested.spec + } + addShrinkwrap(pkg, iferr(next, function () { + addBundled(pkg, iferr(next, function () { + var parent = earliestInstallable(tree, tree, pkg) || tree + var child = createChild({ + package: pkg, + parent: parent, + path: path.join(parent.path, 'node_modules', pkg.name), + realpath: path.resolve(parent.realpath, 'node_modules', pkg.name), + children: pkg._bundled || [], + isLink: tree.isLink, + knownInstallable: true + }) + delete pkg._bundled + var hasBundled = child.children.length + + var replaced = replaceModuleByName(parent, 'children', child) + if (replaced) removeObsoleteDep(replaced) + addRequiredDep(tree, child, function () { + child.location = flatNameFromTree(child) + + if (tree.parent && parent !== tree) updatePhantomChildren(tree.parent, child) + + if (hasBundled) { + inflateBundled(child, child.children) + } + + if (pkg._shrinkwrap && pkg._shrinkwrap.dependencies) { + return inflateShrinkwrap(child, pkg._shrinkwrap.dependencies, function (er) { + next(er, child, log) + }) + } - next(null, child, log) - }) + next(null, child, log) + }) + })) })) })) } diff --git a/deps/npm/lib/install/flatten-tree.js b/deps/npm/lib/install/flatten-tree.js index 869685a4e8..b6ceb30f2f 100644 --- a/deps/npm/lib/install/flatten-tree.js +++ b/deps/npm/lib/install/flatten-tree.js @@ -2,7 +2,11 @@ var validate = require('aproba') var moduleName = require('../utils/module-name.js') -module.exports = function (tree) { +module.exports = flattenTree +module.exports.flatName = flatName +module.exports.flatNameFromTree = flatNameFromTree + +function flattenTree (tree) { validate('O', arguments) var seen = {} var flat = {} @@ -24,7 +28,15 @@ module.exports = function (tree) { return flat } -var flatName = module.exports.flatName = function (path, child) { +function flatName (path, child) { validate('SO', arguments) return path + (moduleName(child) || 'TOP') } + +function flatNameFromTree (tree) { + validate('O', arguments) + if (tree.isTop) return '/' + var path = flatNameFromTree(tree.parent) + if (path !== '/') path += '/' + return flatName(path, tree) +} diff --git a/deps/npm/lib/install/inflate-shrinkwrap.js b/deps/npm/lib/install/inflate-shrinkwrap.js index 1853f2c068..ab1bdd1f19 100644 --- a/deps/npm/lib/install/inflate-shrinkwrap.js +++ b/deps/npm/lib/install/inflate-shrinkwrap.js @@ -1,9 +1,9 @@ 'use strict' -var url = require('url') var asyncMap = require('slide').asyncMap var validate = require('aproba') var iferr = require('iferr') -var realizePackageSpecifier = require('realize-package-specifier') +var realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier.js') +var isRegistrySpecifier = require('./is-registry-specifier.js') var fetchPackageMetadata = require('../fetch-package-metadata.js') var annotateMetadata = require('../fetch-package-metadata.js').annotateMetadata var addShrinkwrap = require('../fetch-package-metadata.js').addShrinkwrap @@ -14,53 +14,80 @@ var createChild = require('./node.js').create var moduleName = require('../utils/module-name.js') var childPath = require('../utils/child-path.js') -var inflateShrinkwrap = module.exports = function (tree, swdeps, finishInflating) { - validate('OOF', arguments) +module.exports = function (tree, swdeps, finishInflating) { if (!npm.config.get('shrinkwrap')) return finishInflating() + return inflateShrinkwrap(tree.path, tree, swdeps, finishInflating) +} + +function inflateShrinkwrap (topPath, tree, swdeps, finishInflating) { + validate('SOOF', arguments) var onDisk = {} tree.children.forEach(function (child) { onDisk[moduleName(child)] = child }) tree.children = [] - asyncMap(Object.keys(swdeps), function (name, next) { - var sw = swdeps[name] - var spec = sw.resolved - ? name + '@' + sw.resolved - : (sw.from && url.parse(sw.from).protocol) - ? name + '@' + sw.from - : name + '@' + sw.version - var child = onDisk[name] - if (child && (child.fromShrinkwrap || - (sw.resolved && child.package._resolved === sw.resolved) || - (sw.from && url.parse(sw.from).protocol && child.package._from === sw.from) || - child.package.version === sw.version)) { - if (!child.fromShrinkwrap) child.fromShrinkwrap = spec - tree.children.push(child) + var dev = npm.config.get('dev') || (!/^prod(uction)?$/.test(npm.config.get('only')) && !npm.config.get('production')) || /^dev(elopment)?$/.test(npm.config.get('only')) + var prod = !/^dev(elopment)?$/.test(npm.config.get('only')) + return asyncMap(Object.keys(swdeps), doRealizeAndInflate, finishInflating) + + function doRealizeAndInflate (name, next) { + return realizeShrinkwrapSpecifier(name, swdeps[name], topPath, iferr(next, andInflate(name, next))) + } - return realizePackageSpecifier(spec, tree.path, iferr(next, function (requested) { - annotateMetadata(child.package, requested, spec, tree.path) - return inflateShrinkwrap(child, sw.dependencies || {}, next) - })) + function andInflate (name, next) { + return function (requested) { + var sw = swdeps[name] + var dependencies = sw.dependencies || {} + if ((!prod && !sw.dev) || (!dev && sw.dev)) return next() + var child = onDisk[name] + if (childIsEquivalent(sw, requested, child)) { + if (!child.fromShrinkwrap) child.fromShrinkwrap = requested.raw + tree.children.push(child) + annotateMetadata(child.package, requested, requested.raw, topPath) + return inflateShrinkwrap(topPath, child, dependencies || {}, next) + } else { + var from = sw.from || requested.raw + return fetchPackageMetadata(requested, topPath, iferr(next, andAddShrinkwrap(from, dependencies, next))) + } } - fetchPackageMetadata(spec, tree.path, iferr(next, function (pkg) { - pkg._from = sw.from || spec - addShrinkwrap(pkg, iferr(next, function () { - addBundled(pkg, iferr(next, function () { - var child = createChild({ - package: pkg, - loaded: false, - parent: tree, - fromShrinkwrap: spec, - path: childPath(tree.path, pkg), - realpath: childPath(tree.realpath, pkg), - children: pkg._bundled || [] - }) - tree.children.push(child) - if (pkg._bundled) { - delete pkg._bundled - inflateBundled(child, child.children) - } - inflateShrinkwrap(child, sw.dependencies || {}, next) - })) - })) - })) - }, finishInflating) + } + + function andAddShrinkwrap (from, dependencies, next) { + return function (pkg) { + pkg._from = from + addShrinkwrap(pkg, iferr(next, andAddBundled(pkg, dependencies, next))) + } + } + + function andAddBundled (pkg, dependencies, next) { + return function () { + return addBundled(pkg, iferr(next, andAddChild(pkg, dependencies, next))) + } + } + + function andAddChild (pkg, dependencies, next) { + return function () { + var child = createChild({ + package: pkg, + loaded: false, + parent: tree, + fromShrinkwrap: pkg._from, + path: childPath(tree.path, pkg), + realpath: childPath(tree.realpath, pkg), + children: pkg._bundled || [] + }) + tree.children.push(child) + if (pkg._bundled) { + delete pkg._bundled + inflateBundled(child, child.children) + } + inflateShrinkwrap(topPath, child, dependencies || {}, next) + } + } +} + +function childIsEquivalent (sw, requested, child) { + if (!child) return false + if (child.fromShrinkwrap) return true + if (sw.resolved) return child.package._resolved === sw.resolved + if (!isRegistrySpecifier(requested) && sw.from) return child.package._from === sw.from + return child.package.version === sw.version } diff --git a/deps/npm/lib/install/is-dev-dep.js b/deps/npm/lib/install/is-dev-dep.js new file mode 100644 index 0000000000..c7a7982c5e --- /dev/null +++ b/deps/npm/lib/install/is-dev-dep.js @@ -0,0 +1,8 @@ +'use strict' +module.exports = isDevDep + +function isDevDep (node, name) { + return node.package && + node.package.devDependencies && + node.package.devDependencies[name] +} diff --git a/deps/npm/lib/install/is-dev.js b/deps/npm/lib/install/is-dev.js deleted file mode 100644 index e0fae4eb82..0000000000 --- a/deps/npm/lib/install/is-dev.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict' -var moduleName = require('../utils/module-name.js') - -function andIsDev (name) { - return function (req) { - return req.package && - req.package.devDependencies && - req.package.devDependencies[name] - } -} - -exports.isDev = function (node) { - return node.requiredBy.some(andIsDev(moduleName(node))) -} - -function andIsOnlyDev (name) { - var isThisDev = andIsDev(name) - return function (req) { - return isThisDev(req) && - (!req.package.dependencies || !req.package.dependencies[name]) - } -} - -exports.isOnlyDev = function (node) { - return node.requiredBy.every(andIsOnlyDev(moduleName(node))) -} diff --git a/deps/npm/lib/install/is-opt-dep.js b/deps/npm/lib/install/is-opt-dep.js new file mode 100644 index 0000000000..807b8ab998 --- /dev/null +++ b/deps/npm/lib/install/is-opt-dep.js @@ -0,0 +1,8 @@ +'use strict' +module.exports = isOptDep + +function isOptDep (node, name) { + return node.package && + node.package.optionalDependencies && + node.package.optionalDependencies[name] +} diff --git a/deps/npm/lib/install/is-prod-dep.js b/deps/npm/lib/install/is-prod-dep.js new file mode 100644 index 0000000000..1e2a0b4e49 --- /dev/null +++ b/deps/npm/lib/install/is-prod-dep.js @@ -0,0 +1,9 @@ +'use strict' + +module.exports = isProdDep + +function isProdDep (node, name) { + return node.package && + node.package.dependencies && + node.package.dependencies[name] +} diff --git a/deps/npm/lib/install/is-registry-specifier.js b/deps/npm/lib/install/is-registry-specifier.js new file mode 100644 index 0000000000..606be2bd13 --- /dev/null +++ b/deps/npm/lib/install/is-registry-specifier.js @@ -0,0 +1,6 @@ +'use strict' +module.exports = isRegistrySpecifier + +function isRegistrySpecifier (spec) { + return spec.type === 'range' || spec.type === 'version' || spec.type === 'tag' +} diff --git a/deps/npm/lib/install/realize-shrinkwrap-specifier.js b/deps/npm/lib/install/realize-shrinkwrap-specifier.js new file mode 100644 index 0000000000..0c491a6028 --- /dev/null +++ b/deps/npm/lib/install/realize-shrinkwrap-specifier.js @@ -0,0 +1,25 @@ +'use strict' +var realizePackageSpecifier = require('realize-package-specifier') +var isRegistrySpecifier = require('./is-registry-specifier.js') + +module.exports = function (name, sw, where, cb) { + function lookup (ver, cb) { + realizePackageSpecifier(name + '@' + ver, where, cb) + } + if (sw.resolved) { + return lookup(sw.resolved, cb) + } else if (sw.from) { + return lookup(sw.from, function (err, spec) { + if (err || isRegistrySpecifier(spec)) { + return thenUseVersion() + } else { + return cb(null, spec) + } + }) + } else { + return thenUseVersion() + } + function thenUseVersion () { + lookup(sw.version, cb) + } +} diff --git a/deps/npm/lib/install/report-optional-failure.js b/deps/npm/lib/install/report-optional-failure.js new file mode 100644 index 0000000000..41c7c535bd --- /dev/null +++ b/deps/npm/lib/install/report-optional-failure.js @@ -0,0 +1,31 @@ +'use strict' +var path = require('path') +var moduleName = require('../utils/module-name.js') + +module.exports = reportOptionalFailure + +function top (tree) { + if (tree.parent) return top(tree.parent) + return tree +} + +function reportOptionalFailure (tree, what, error) { + var topTree = top(tree) + if (!topTree.warnings) topTree.warnings = [] + var id + if (what) { + var depVer = tree.package.dependencies && tree.package.dependencies[what] + var optDepVer = tree.package.optionalDependencies && tree.package.optionalDependencies[what] + var devDepVer = tree.package.devDependencies && tree.package.devDependencies[what] + var version = depVer || optDepVer || devDepVer + id = what + (version ? '@' + version : '') + } else { + id = tree._id || moduleName(tree) + (tree.package.version ? '@' + tree.package.version : '') + } + var location = path.relative(topTree.path, tree.path) + if (what) location = path.join(location, 'node_modules', what) + + error.optional = id + error.location = location + topTree.warnings.push(error) +} diff --git a/deps/npm/lib/install/save.js b/deps/npm/lib/install/save.js index 708da61c8a..d5c97bfaa2 100644 --- a/deps/npm/lib/install/save.js +++ b/deps/npm/lib/install/save.js @@ -12,7 +12,8 @@ var npm = require('../npm.js') var deepSortObject = require('../utils/deep-sort-object.js') var parseJSON = require('../utils/parse-json.js') var moduleName = require('../utils/module-name.js') -var isOnlyDev = require('./is-dev.js').isOnlyDev +var isDevDep = require('./is-dev-dep.js') +var createShrinkwrap = require('../shrinkwrap.js').createShrinkwrap // if the -S|--save option is specified, then write installed packages // as dependencies to a package.json file. @@ -49,25 +50,17 @@ function saveShrinkwrap (tree, next) { var saveOptional = npm.config.get('save-optional') var shrinkwrap = tree.package._shrinkwrap || {dependencies: {}} - var hasDevOnlyDeps = tree.requires.filter(function (dep) { - return isOnlyDev(dep) - }).some(function (dep) { - return shrinkwrap.dependencies[dep.package.name] != null + var shrinkwrapHasAnyDevOnlyDeps = tree.requires.some(function (dep) { + var name = moduleName(dep) + return isDevDep(tree, name) && + shrinkwrap.dependencies[name] != null }) - if (!saveOptional && saveDev && !hasDevOnlyDeps) return next() - if (saveOptional || !save) return next() + if (!saveOptional && saveDev && !shrinkwrapHasAnyDevOnlyDeps) return next() + if (saveOptional || !(save || saveDev)) return next() - if (hasDevOnlyDeps) { - var dev = npm.config.get('dev') - npm.config.set('dev', true) - npm.commands.shrinkwrap([], true, function () { - npm.config.set('dev', dev) - next.apply(this, arguments) - }) - } else { - npm.commands.shrinkwrap([], true, next) - } + var silent = false + createShrinkwrap(tree.path, tree.package, shrinkwrapHasAnyDevOnlyDeps, silent, next) }) } diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js index c6a41842fc..02481a235e 100644 --- a/deps/npm/lib/shrinkwrap.js +++ b/deps/npm/lib/shrinkwrap.js @@ -15,11 +15,13 @@ var npm = require('./npm.js') var recalculateMetadata = require('./install/deps.js').recalculateMetadata var validatePeerDeps = require('./install/deps.js').validatePeerDeps var isExtraneous = require('./install/is-extraneous.js') -var isOnlyDev = require('./install/is-dev.js').isOnlyDev var packageId = require('./utils/package-id.js') var moduleName = require('./utils/module-name.js') var output = require('./utils/output.js') var lifecycle = require('./utils/lifecycle.js') +var isDevDep = require('./install/is-dev-dep.js') +var isProdDep = require('./install/is-prod-dep.js') +var isOptDep = require('./install/is-opt-dep.js') shrinkwrap.usage = 'npm shrinkwrap' @@ -35,25 +37,31 @@ function shrinkwrap (args, silent, cb) { var dir = path.resolve(npm.dir, '..') var packagePath = path.join(npm.localPrefix, 'package.json') - npm.config.set('production', true) - - readPackageJson(packagePath, iferr(cb, function (data) { - lifecycle(data, 'preshrinkwrap', function () { - readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (tree) { - var pkginfo = treeToShrinkwrap(tree, !!npm.config.get('dev') || /^dev(elopment)?$/.test(npm.config.get('also'))) - - chain([ - [lifecycle, tree.package, 'shrinkwrap'], - [shrinkwrap_, pkginfo, silent], - [lifecycle, tree.package, 'postshrinkwrap'] - ], iferr(cb, function (data) { - cb(null, data[0]) - })) - }))) - }) + var dev = !!npm.config.get('dev') || /^dev(elopment)?$/.test(npm.config.get('also')) + + readPackageJson(packagePath, iferr(cb, function (pkg) { + createShrinkwrap(dir, pkg, dev, silent, cb) })) } +module.exports.createShrinkwrap = createShrinkwrap + +function createShrinkwrap (dir, pkg, dev, silent, cb) { + lifecycle(pkg, 'preshrinkwrap', function () { + readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (tree) { + var pkginfo = treeToShrinkwrap(tree, dev) + + chain([ + [lifecycle, tree.package, 'shrinkwrap'], + [shrinkwrap_, pkginfo, silent], + [lifecycle, tree.package, 'postshrinkwrap'] + ], iferr(cb, function (data) { + cb(null, data[0]) + })) + }))) + }) +} + function andRecalculateMetadata (next) { validate('F', arguments) return function (er, tree) { @@ -92,14 +100,17 @@ function shrinkwrapDeps (dev, problems, deps, tree, seen) { } }) tree.children.sort(function (aa, bb) { return moduleName(aa).localeCompare(moduleName(bb)) }).forEach(function (child) { - if (!dev && isOnlyDev(child)) { - log.warn('shrinkwrap', 'Excluding devDependency: %s', packageId(child), child.parent.package.dependencies) + var childIsOnlyDev = isOnlyDev(child) + if (!dev && childIsOnlyDev) { + log.warn('shrinkwrap', 'Excluding devDependency: %s', child.location) return } var pkginfo = deps[moduleName(child)] = {} pkginfo.version = child.package.version pkginfo.from = child.package._from pkginfo.resolved = child.package._resolved + if (dev && childIsOnlyDev) pkginfo.dev = true + if (isOptional(child)) pkginfo.optional = true if (isExtraneous(child)) { problems.push('extraneous: ' + child.package._id + ' ' + child.path) } @@ -143,3 +154,46 @@ function save (pkginfo, silent, cb) { cb(null, pkginfo) }) } + +// Returns true if the module `node` is only required direcctly as a dev +// dependency of the top level or transitively _from_ top level dev +// dependencies. +// Dual mode modules (that are both dev AND prod) should return false. +function isOnlyDev (node, seen) { + if (!seen) seen = {} + return node.requiredBy.length && node.requiredBy.every(andIsOnlyDev(moduleName(node), seen)) +} + +// There is a known limitation with this implementation: If a dependency is +// ONLY required by cycles that are detached from the top level then it will +// ultimately return ture. +// +// This is ok though: We don't allow shrinkwraps with extraneous deps and +// these situation is caught by the extraneous checker before we get here. +function andIsOnlyDev (name, seen) { + return function (req) { + var isDev = isDevDep(req, name) + var isProd = isProdDep(req, name) + if (req.isTop) { + return isDev && !isProd + } else { + if (seen[req.path]) return true + seen[req.path] = true + return isOnlyDev(req, seen) + } + } +} + +function isOptional (node, seen) { + if (!seen) seen = {} + // If a node is not required by anything, then we've reached + // the top level package. + if (seen[node.path] || node.requiredBy.length === 0) { + return false + } + seen[node.path] = true + + return node.requiredBy.every(function (req) { + return isOptDep(req, node.package.name) || isOptional(req, seen) + }) +} diff --git a/deps/npm/lib/utils/correct-mkdir.js b/deps/npm/lib/utils/correct-mkdir.js index c0a31bdc58..68c4a4ad79 100644 --- a/deps/npm/lib/utils/correct-mkdir.js +++ b/deps/npm/lib/utils/correct-mkdir.js @@ -43,8 +43,13 @@ function calculateOwner () { if (!effectiveOwner) { effectiveOwner = { uid: 0, gid: 0 } - if (process.getuid) effectiveOwner.uid = +process.getuid() - if (process.getgid) effectiveOwner.gid = +process.getgid() + // Pretty much only on windows + if (!process.getuid) { + return effectiveOwner + } + + effectiveOwner.uid = +process.getuid() + effectiveOwner.gid = +process.getgid() if (effectiveOwner.uid === 0) { if (process.env.SUDO_UID) effectiveOwner.uid = +process.env.SUDO_UID @@ -112,6 +117,7 @@ function makeDirectory (path, cb) { function setPermissions (path, st, cb) { chownr(path, st.uid, st.gid, function (er) { + if (er && er.code === 'ENOENT') return cb(null, st) return cb(er, st) }) } diff --git a/deps/npm/lib/utils/error-message.js b/deps/npm/lib/utils/error-message.js index fca92ee4cb..5c7ce15bf1 100644 --- a/deps/npm/lib/utils/error-message.js +++ b/deps/npm/lib/utils/error-message.js @@ -8,9 +8,6 @@ module.exports = errorMessage function errorMessage (er) { var short = [] var detail = [] - if (er.optional) { - short.push(['optional', 'Skipping failed optional dependency ' + er.optional + ':']) - } switch (er.code) { case 'ECONNREFUSED': short.push(['', er]) @@ -150,17 +147,21 @@ function errorMessage (er) { break case 'EBADPLATFORM': + var validOs = er.os.join ? er.os.join(',') : er.os + var validArch = er.cpu.join ? er.cpu.join(',') : er.cpu + var expected = {os: validOs, arch: validArch} + var actual = {os: process.platform, arch: process.arch} short.push([ 'notsup', [ - 'Not compatible with your operating system or architecture: ' + er.pkgid + util.format('Unsupported platform for %s: wanted %j (current: %j)', er.pkgid, expected, actual) ].join('\n') ]) detail.push([ 'notsup', [ - 'Valid OS: ' + (er.os.join ? er.os.join(',') : util.inspect(er.os)), - 'Valid Arch: ' + (er.cpu.join ? er.cpu.join(',') : util.inspect(er.cpu)), + 'Valid OS: ' + validOs, + 'Valid Arch: ' + validArch, 'Actual OS: ' + process.platform, 'Actual Arch: ' + process.arch ].join('\n') @@ -284,7 +285,7 @@ function errorMessage (er) { 'typeerror', [ 'This is an error with npm itself. Please report this error at:', - ' <http://github.com/npm/npm/issues>' + ' <https://github.com/npm/npm/issues>' ].join('\n') ]) break @@ -313,5 +314,12 @@ function errorMessage (er) { ]) break } + if (er.optional) { + short.unshift(['optional', er.optional + ' (' + er.location + '):']) + short.concat(detail).forEach(function (msg) { + if (!msg[0]) msg[0] = 'optional' + if (msg[1]) msg[1] = msg[1].toString().replace(/(^|\n)/g, '$1SKIPPING OPTIONAL DEPENDENCY: ') + }) + } return {summary: short, detail: detail} } diff --git a/deps/npm/lib/utils/lifecycle.js b/deps/npm/lib/utils/lifecycle.js index 6a862366f2..adf534d882 100644 --- a/deps/npm/lib/utils/lifecycle.js +++ b/deps/npm/lib/utils/lifecycle.js @@ -15,6 +15,7 @@ var uidNumber = require('uid-number') var umask = require('./umask') var usage = require('./usage') var output = require('./output.js') +var which = require('which') // windows calls it's path 'Path' usually, but this is not guaranteed. if (process.platform === 'win32') { @@ -88,7 +89,7 @@ function _incorrectWorkingDirectory (wd, pkg) { function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) { var pathArr = [] - var p = wd.split('node_modules') + var p = wd.split(/[\\\/]node_modules[\\\/]/) var acc = path.resolve(p.shift()) p.forEach(function (pp) { @@ -101,8 +102,10 @@ function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) { // the bundled one will be used for installing things. pathArr.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin')) - // prefer current node interpreter in child scripts - pathArr.push(path.dirname(process.execPath)) + if (shouldPrependCurrentNodeDirToPATH()) { + // prefer current node interpreter in child scripts + pathArr.push(path.dirname(process.execPath)) + } if (env[PATH]) pathArr.push(env[PATH]) env[PATH] = pathArr.join(process.platform === 'win32' ? ';' : ':') @@ -138,6 +141,16 @@ function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) { ) } +function shouldPrependCurrentNodeDirToPATH () { + var isWindows = process.platform === 'win32' + try { + var foundExecPath = which.sync(path.basename(process.execPath), {pathExt: isWindows ? ';' : ':'}) + return process.execPath.toUpperCase() !== foundExecPath.toUpperCase() + } catch (e) { + return true + } +} + function validWd (d, cb) { fs.stat(d, function (er, st) { if (er || !st.isDirectory()) { diff --git a/deps/npm/lib/utils/tar.js b/deps/npm/lib/utils/tar.js index 1e00040dbb..f580c8e05d 100644 --- a/deps/npm/lib/utils/tar.js +++ b/deps/npm/lib/utils/tar.js @@ -102,7 +102,8 @@ BundledPacker.prototype.applyIgnores = function (entry, partial, entryObj) { entry === '.npmrc' || entry.match(/^\..*\.swp$/) || entry === '.DS_Store' || - entry.match(/^\._/) + entry.match(/^\._/) || + entry.match(/^.*\.orig$/) ) { return false } @@ -117,7 +118,17 @@ BundledPacker.prototype.applyIgnores = function (entry, partial, entryObj) { // if they're not already present at a higher level. if (this.bundleMagic) { // bubbling up. stop here and allow anything the bundled pkg allows - if (entry.indexOf('/') !== -1) return true + if (entry.charAt(0) === '@') { + var firstSlash = entry.indexOf('/') + // continue to list the packages in this scope + if (firstSlash === -1) return true + + // bubbling up. stop here and allow anything the bundled pkg allows + if (entry.indexOf('/', firstSlash + 1) !== -1) return true + // bubbling up. stop here and allow anything the bundled pkg allows + } else if (entry.indexOf('/') !== -1) { + return true + } // never include the .bin. It's typically full of platform-specific // stuff like symlinks and .cmd files anyway. |