summaryrefslogtreecommitdiff
path: root/deps/npm/lib/utils
diff options
context:
space:
mode:
authorRebecca Turner <me@re-becca.org>2016-01-28 18:11:35 -0800
committerJeremiah Senkpiel <fishrock123@rocketmail.com>2016-02-01 10:43:34 -0500
commit76cb81b354de8447898427619c66c86c59b22b3d (patch)
tree0c0826bed77086fb38cda8dc680d4fb10d2cff25 /deps/npm/lib/utils
parentd5d301f3032a0723f5a29cfbe0d95ac4ad205d42 (diff)
downloadandroid-node-v8-76cb81b354de8447898427619c66c86c59b22b3d.tar.gz
android-node-v8-76cb81b354de8447898427619c66c86c59b22b3d.tar.bz2
android-node-v8-76cb81b354de8447898427619c66c86c59b22b3d.zip
deps: upgrade npm to 3.6.0
PR-URL: https://github.com/nodejs/node/pull/4958 Reviewed-By: Myles Borins <mborins@us.ibm.com> Reviewed-By: Kat Marchán <kzm@sykosomatic.org> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Diffstat (limited to 'deps/npm/lib/utils')
-rw-r--r--deps/npm/lib/utils/correct-mkdir.js7
-rw-r--r--deps/npm/lib/utils/error-handler.js323
-rw-r--r--deps/npm/lib/utils/error-message.js317
-rw-r--r--deps/npm/lib/utils/gently-rm.js348
-rw-r--r--deps/npm/lib/utils/lifecycle.js5
-rw-r--r--deps/npm/lib/utils/rename.js16
-rw-r--r--deps/npm/lib/utils/save-stack.js16
7 files changed, 580 insertions, 452 deletions
diff --git a/deps/npm/lib/utils/correct-mkdir.js b/deps/npm/lib/utils/correct-mkdir.js
index 650c56fb17..c0a31bdc58 100644
--- a/deps/npm/lib/utils/correct-mkdir.js
+++ b/deps/npm/lib/utils/correct-mkdir.js
@@ -10,6 +10,13 @@ var stats = {}
var effectiveOwner
module.exports = function correctMkdir (path, cb) {
cb = dezalgo(cb)
+ cb = inflight('correctMkdir:' + path, cb)
+ if (!cb) {
+ return log.verbose('correctMkdir', path, 'correctMkdir already in flight; waiting')
+ } else {
+ log.verbose('correctMkdir', path, 'correctMkdir not in flight; initializing')
+ }
+
if (stats[path]) return cb(null, stats[path])
fs.stat(path, function (er, st) {
diff --git a/deps/npm/lib/utils/error-handler.js b/deps/npm/lib/utils/error-handler.js
index 8a7b1c06da..91b180e1b3 100644
--- a/deps/npm/lib/utils/error-handler.js
+++ b/deps/npm/lib/utils/error-handler.js
@@ -11,8 +11,8 @@ var wroteLogFile = false
var exitCode = 0
var rollbacks = npm.rollbacks
var chain = require('slide').chain
-var writeStream = require('fs-write-stream-atomic')
-var nameValidator = require('validate-npm-package-name')
+var writeStreamAtomic = require('fs-write-stream-atomic')
+var errorMessage = require('./error-message.js')
process.on('exit', function (code) {
log.disableProgress()
@@ -170,319 +170,10 @@ function errorHandler (er) {
// just a line break
if (log.levels[log.level] <= log.levels.error) console.error('')
- switch (er.code) {
- case 'ECONNREFUSED':
- log.error('', er)
- log.error(
- '',
- [
- '\nIf you are behind a proxy, please make sure that the',
- "'proxy' config is set properly. See: 'npm help config'"
- ].join('\n')
- )
- break
-
- case 'EACCES':
- case 'EPERM':
- log.error('', er)
- log.error('', ['\nPlease try running this command again as root/Administrator.'
- ].join('\n'))
- break
-
- case 'ELIFECYCLE':
- log.error('', er.message)
- log.error(
- '',
- [
- '',
- 'Failed at the ' + er.pkgid + ' ' + er.stage + " script '" + er.script + "'.",
- 'Make sure you have the latest version of node.js and npm installed.',
- 'If you do, this is most likely a problem with the ' + er.pkgname + ' package,',
- 'not with npm itself.',
- 'Tell the author that this fails on your system:',
- ' ' + er.script,
- 'You can get their info via:',
- ' npm owner ls ' + er.pkgname,
- 'There is likely additional logging output above.'
- ].join('\n')
- )
- break
-
- case 'ENOGIT':
- log.error('', er.message)
- log.error(
- '',
- [
- '',
- 'Failed using git.',
- 'This is most likely not a problem with npm itself.',
- 'Please check if you have git installed and in your PATH.'
- ].join('\n')
- )
- break
-
- case 'EJSONPARSE':
- log.error('', er.message)
- log.error('', 'File: ' + er.file)
- log.error(
- '',
- [
- 'Failed to parse package.json data.',
- 'package.json must be actual JSON, not just JavaScript.',
- '',
- 'This is not a bug in npm.',
- 'Tell the package author to fix their package.json file.'
- ].join('\n'),
- 'JSON.parse'
- )
- break
-
- // TODO(isaacs)
- // Add a special case here for E401 and E403 explaining auth issues?
-
- case 'E404':
- var msg = [er.message]
- if (er.pkgid && er.pkgid !== '-') {
- msg.push('', "'" + er.pkgid + "' is not in the npm registry.")
-
- var valResult = nameValidator(er.pkgid)
-
- if (valResult.validForNewPackages) {
- msg.push('You should bug the author to publish it (or use the name yourself!)')
- } else {
- msg.push('Your package name is not valid, because', '')
-
- var errorsArray = (valResult.errors || []).concat(valResult.warnings || [])
- errorsArray.forEach(function (item, idx) {
- msg.push(' ' + (idx + 1) + '. ' + item)
- })
- }
-
- if (er.parent) {
- msg.push("It was specified as a dependency of '" + er.parent + "'")
- }
- msg.push(
- '\nNote that you can also install from a',
- 'tarball, folder, http url, or git url.'
- )
- }
- // There's no need to have 404 in the message as well.
- msg[0] = msg[0].replace(/^404\s+/, '')
- log.error('404', msg.join('\n'))
- break
-
- case 'EPUBLISHCONFLICT':
- log.error(
- 'publish fail',
- [
- 'Cannot publish over existing version.',
- "Update the 'version' field in package.json and try again.",
- '',
- 'To automatically increment version numbers, see:',
- ' npm help version'
- ].join('\n')
- )
- break
-
- case 'EISGIT':
- log.error(
- 'git',
- [
- er.message,
- ' ' + er.path,
- 'Refusing to remove it. Update manually,',
- 'or move it out of the way first.'
- ].join('\n')
- )
- break
-
- case 'ECYCLE':
- log.error(
- 'cycle',
- [
- er.message,
- 'While installing: ' + er.pkgid,
- 'Found a pathological dependency case that npm cannot solve.',
- 'Please report this to the package author.'
- ].join('\n')
- )
- break
-
- case 'EBADPLATFORM':
- log.error(
- 'notsup',
- [
- er.message,
- 'Not compatible with your operating system or architecture: ' + er.pkgid,
- 'Valid OS: ' + er.os.join(','),
- 'Valid Arch: ' + er.cpu.join(','),
- 'Actual OS: ' + process.platform,
- 'Actual Arch: ' + process.arch
- ].join('\n')
- )
- break
-
- case 'EEXIST':
- log.error(
- [
- er.message,
- 'File exists: ' + er.path,
- 'Move it away, and try again.'
- ].join('\n')
- )
- break
-
- case 'ENEEDAUTH':
- log.error(
- 'need auth',
- [
- er.message,
- 'You need to authorize this machine using `npm adduser`'
- ].join('\n')
- )
- break
-
- case 'EPEERINVALID':
- var peerErrors = Object.keys(er.peersDepending).map(function (peer) {
- return 'Peer ' + peer + ' wants ' +
- er.packageName + '@' + er.peersDepending[peer]
- })
- log.error('peerinvalid', [er.message].concat(peerErrors).join('\n'))
- break
-
- case 'ECONNRESET':
- case 'ENOTFOUND':
- case 'ETIMEDOUT':
- case 'EAI_FAIL':
- log.error(
- 'network',
- [
- er.message,
- 'This is most likely not a problem with npm itself',
- 'and is related to network connectivity.',
- 'In most cases you are behind a proxy or have bad network settings.',
- '\nIf you are behind a proxy, please make sure that the',
- "'proxy' config is set properly. See: 'npm help config'"
- ].join('\n')
- )
- break
-
- case 'ENOPACKAGEJSON':
- log.error(
- 'package.json',
- [
- er.message,
- 'This is most likely not a problem with npm itself.',
- "npm can't find a package.json file in your current directory."
- ].join('\n')
- )
- break
-
- case 'ETARGET':
- msg = [
- er.message,
- 'This is most likely not a problem with npm itself.',
- 'In most cases you or one of your dependencies are requesting',
- "a package version that doesn't exist."
- ]
- if (er.parent) {
- msg.push("\nIt was specified as a dependency of '" + er.parent + "'\n")
- }
- log.error('notarget', msg.join('\n'))
- break
-
- case 'ENOTSUP':
- if (er.required) {
- log.error(
- 'notsup',
- [
- er.message,
- 'Not compatible with your version of node/npm: ' + er.pkgid,
- 'Required: ' + JSON.stringify(er.required),
- 'Actual: ' + JSON.stringify({
- npm: npm.version,
- node: npm.config.get('node-version')
- })
- ].join('\n')
- )
- break
- } // else passthrough
- /*eslint no-fallthrough:0*/
-
- case 'ENOSPC':
- log.error(
- 'nospc',
- [
- er.message,
- 'This is most likely not a problem with npm itself',
- 'and is related to insufficient space on your system.'
- ].join('\n')
- )
- break
-
- case 'EROFS':
- log.error(
- 'rofs',
- [
- er.message,
- 'This is most likely not a problem with npm itself',
- 'and is related to the file system being read-only.',
- '\nOften virtualized file systems, or other file systems',
- "that don't support symlinks, give this error."
- ].join('\n')
- )
- break
-
- case 'ENOENT':
- log.error(
- 'enoent',
- [
- er.message,
- 'This is most likely not a problem with npm itself',
- 'and is related to npm not being able to find a file.',
- er.file ? "\nCheck if the file '" + er.file + "' is present." : ''
- ].join('\n')
- )
- break
-
- case 'EMISSINGARG':
- case 'EUNKNOWNTYPE':
- case 'EINVALIDTYPE':
- case 'ETOOMANYARGS':
- log.error(
- 'typeerror',
- [
- er.stack,
- 'This is an error with npm itself. Please report this error at:',
- ' <http://github.com/npm/npm/issues>'
- ].join('\n')
- )
- break
-
- case 'EISDIR':
- log.error(
- 'eisdir',
- [
- er.message,
- 'This is most likely not a problem with npm itself',
- 'and is related to npm not being able to find a package.json in',
- 'a package you are trying to install.'
- ].join('\n')
- )
- break
-
- default:
- log.error('', er.message || er)
- log.error(
- '',
- [
- '',
- 'If you need help, you may report this error at:',
- ' <https://github.com/npm/npm/issues>'
- ].join('\n')
- )
- break
- }
+ var msg = errorMessage(er)
+ msg.summary.concat(msg.detail).forEach(function (errline) {
+ log.error.apply(log, errline)
+ })
exit(typeof er.errno === 'number' ? er.errno : 1)
}
@@ -493,7 +184,7 @@ function writeLogFile (cb) {
writingLogFile = true
wroteLogFile = true
- var fstr = writeStream('npm-debug.log')
+ var fstr = writeStreamAtomic('npm-debug.log')
var os = require('os')
var out = ''
diff --git a/deps/npm/lib/utils/error-message.js b/deps/npm/lib/utils/error-message.js
new file mode 100644
index 0000000000..fca92ee4cb
--- /dev/null
+++ b/deps/npm/lib/utils/error-message.js
@@ -0,0 +1,317 @@
+'use strict'
+var npm = require('../npm.js')
+var util = require('util')
+var nameValidator = require('validate-npm-package-name')
+
+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])
+ detail.push([
+ '',
+ [
+ '\nIf you are behind a proxy, please make sure that the',
+ "'proxy' config is set properly. See: 'npm help config'"
+ ].join('\n')
+ ])
+ break
+
+ case 'EACCES':
+ case 'EPERM':
+ short.push(['', er])
+ detail.push(['', ['\nPlease try running this command again as root/Administrator.'
+ ].join('\n')])
+ break
+
+ case 'ELIFECYCLE':
+ short.push(['', er.message])
+ detail.push([
+ '',
+ [
+ '',
+ 'Failed at the ' + er.pkgid + ' ' + er.stage + " script '" + er.script + "'.",
+ 'Make sure you have the latest version of node.js and npm installed.',
+ 'If you do, this is most likely a problem with the ' + er.pkgname + ' package,',
+ 'not with npm itself.',
+ 'Tell the author that this fails on your system:',
+ ' ' + er.script,
+ 'You can get information on how to open an issue for this project with:',
+ ' npm bugs ' + er.pkgname,
+ 'Or if that isn\'t available, you can get their info via:',
+ ' npm owner ls ' + er.pkgname,
+ 'There is likely additional logging output above.'
+ ].join('\n')]
+ )
+ break
+
+ case 'ENOGIT':
+ short.push(['', er.message])
+ detail.push([
+ '',
+ [
+ '',
+ 'Failed using git.',
+ 'This is most likely not a problem with npm itself.',
+ 'Please check if you have git installed and in your PATH.'
+ ].join('\n')
+ ])
+ break
+
+ case 'EJSONPARSE':
+ short.push(['', er.message])
+ short.push(['', 'File: ' + er.file])
+ detail.push([
+ '',
+ [
+ 'Failed to parse package.json data.',
+ 'package.json must be actual JSON, not just JavaScript.',
+ '',
+ 'This is not a bug in npm.',
+ 'Tell the package author to fix their package.json file.'
+ ].join('\n'),
+ 'JSON.parse'
+ ])
+ break
+
+ // TODO(isaacs)
+ // Add a special case here for E401 and E403 explaining auth issues?
+
+ case 'E404':
+ // There's no need to have 404 in the message as well.
+ var msg = er.message.replace(/^404\s+/, '')
+ short.push(['404', msg])
+ if (er.pkgid && er.pkgid !== '-') {
+ detail.push(['404', ''])
+ detail.push(['404', '', "'" + er.pkgid + "' is not in the npm registry."])
+
+ var valResult = nameValidator(er.pkgid)
+
+ if (valResult.validForNewPackages) {
+ detail.push(['404', 'You should bug the author to publish it (or use the name yourself!)'])
+ } else {
+ detail.push(['404', 'Your package name is not valid, because', ''])
+
+ var errorsArray = (valResult.errors || []).concat(valResult.warnings || [])
+ errorsArray.forEach(function (item, idx) {
+ detail.push(['404', ' ' + (idx + 1) + '. ' + item])
+ })
+ }
+
+ if (er.parent) {
+ detail.push(['404', "It was specified as a dependency of '" + er.parent + "'"])
+ }
+ detail.push(['404', '\nNote that you can also install from a'])
+ detail.push(['404', 'tarball, folder, http url, or git url.'])
+ }
+ break
+
+ case 'EPUBLISHCONFLICT':
+ short.push(['publish fail', 'Cannot publish over existing version.'])
+ detail.push(['publish fail', "Update the 'version' field in package.json and try again."])
+ detail.push(['publish fail', ''])
+ detail.push(['publish fail', 'To automatically increment version numbers, see:'])
+ detail.push(['publish fail', ' npm help version'])
+ break
+
+ case 'EISGIT':
+ short.push(['git', er.message])
+ short.push(['git', ' ' + er.path])
+ detail.push([
+ 'git',
+ [
+ 'Refusing to remove it. Update manually,',
+ 'or move it out of the way first.'
+ ].join('\n')
+ ])
+ break
+
+ case 'ECYCLE':
+ short.push([
+ 'cycle',
+ [
+ er.message,
+ 'While installing: ' + er.pkgid
+ ].join('\n')
+ ])
+ detail.push([
+ 'cycle',
+ [
+ 'Found a pathological dependency case that npm cannot solve.',
+ 'Please report this to the package author.'
+ ].join('\n')
+ ])
+ break
+
+ case 'EBADPLATFORM':
+ short.push([
+ 'notsup',
+ [
+ 'Not compatible with your operating system or architecture: ' + er.pkgid
+ ].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)),
+ 'Actual OS: ' + process.platform,
+ 'Actual Arch: ' + process.arch
+ ].join('\n')
+ ])
+ break
+
+ case 'EEXIST':
+ short.push(['', er.message])
+ short.push(['', 'File exists: ' + er.path])
+ detail.push(['', 'Move it away, and try again.'])
+ break
+
+ case 'ENEEDAUTH':
+ short.push(['need auth', er.message])
+ detail.push(['need auth', 'You need to authorize this machine using `npm adduser`'])
+ break
+
+ case 'ECONNRESET':
+ case 'ENOTFOUND':
+ case 'ETIMEDOUT':
+ case 'EAI_FAIL':
+ short.push(['network', er.message])
+ detail.push([
+ 'network',
+ [
+ 'This is most likely not a problem with npm itself',
+ 'and is related to network connectivity.',
+ 'In most cases you are behind a proxy or have bad network settings.',
+ '\nIf you are behind a proxy, please make sure that the',
+ "'proxy' config is set properly. See: 'npm help config'"
+ ].join('\n')
+ ])
+ break
+
+ case 'ENOPACKAGEJSON':
+ short.push(['package.json', er.message])
+ detail.push([
+ 'package.json',
+ [
+ 'This is most likely not a problem with npm itself.',
+ "npm can't find a package.json file in your current directory."
+ ].join('\n')
+ ])
+ break
+
+ case 'ETARGET':
+ short.push(['notarget', er.message])
+ msg = [
+ 'This is most likely not a problem with npm itself.',
+ 'In most cases you or one of your dependencies are requesting',
+ "a package version that doesn't exist."
+ ]
+ if (er.parent) {
+ msg.push("\nIt was specified as a dependency of '" + er.parent + "'\n")
+ }
+ detail.push(['notarget', msg.join('\n')])
+ break
+
+ case 'ENOTSUP':
+ if (er.required) {
+ short.push(['notsup', er.message])
+ short.push(['notsup', 'Not compatible with your version of node/npm: ' + er.pkgid])
+ detail.push([
+ 'notsup',
+ [
+ 'Not compatible with your version of node/npm: ' + er.pkgid,
+ 'Required: ' + JSON.stringify(er.required),
+ 'Actual: ' + JSON.stringify({
+ npm: npm.version,
+ node: npm.config.get('node-version')
+ })
+ ].join('\n')
+ ])
+ break
+ } // else passthrough
+ /*eslint no-fallthrough:0*/
+
+ case 'ENOSPC':
+ short.push(['nospc', er.message])
+ detail.push([
+ 'nospc',
+ [
+ 'This is most likely not a problem with npm itself',
+ 'and is related to insufficient space on your system.'
+ ].join('\n')
+ ])
+ break
+
+ case 'EROFS':
+ short.push(['rofs', er.message])
+ detail.push([
+ 'rofs',
+ [
+ 'This is most likely not a problem with npm itself',
+ 'and is related to the file system being read-only.',
+ '\nOften virtualized file systems, or other file systems',
+ "that don't support symlinks, give this error."
+ ].join('\n')
+ ])
+ break
+
+ case 'ENOENT':
+ short.push(['enoent', er.message])
+ detail.push([
+ 'enoent',
+ [
+ er.message,
+ 'This is most likely not a problem with npm itself',
+ 'and is related to npm not being able to find a file.',
+ er.file ? "\nCheck if the file '" + er.file + "' is present." : ''
+ ].join('\n')
+ ])
+ break
+
+ case 'EMISSINGARG':
+ case 'EUNKNOWNTYPE':
+ case 'EINVALIDTYPE':
+ case 'ETOOMANYARGS':
+ short.push(['typeerror', er.stack])
+ detail.push([
+ 'typeerror',
+ [
+ 'This is an error with npm itself. Please report this error at:',
+ ' <http://github.com/npm/npm/issues>'
+ ].join('\n')
+ ])
+ break
+
+ case 'EISDIR':
+ short.push(['eisdir', er.message])
+ detail.push([
+ 'eisdir',
+ [
+ 'This is most likely not a problem with npm itself',
+ 'and is related to npm not being able to find a package.json in',
+ 'a package you are trying to install.'
+ ].join('\n')
+ ])
+ break
+
+ default:
+ short.push(['', er.message || er])
+ detail.push([
+ '',
+ [
+ '',
+ 'If you need help, you may report this error at:',
+ ' <https://github.com/npm/npm/issues>'
+ ].join('\n')
+ ])
+ break
+ }
+ return {summary: short, detail: detail}
+}
diff --git a/deps/npm/lib/utils/gently-rm.js b/deps/npm/lib/utils/gently-rm.js
index c73bb1bfc0..634bf94fcc 100644
--- a/deps/npm/lib/utils/gently-rm.js
+++ b/deps/npm/lib/utils/gently-rm.js
@@ -1,21 +1,22 @@
-// only remove the thing if it's a symlink into a specific folder.
-// This is a very common use-case of npm's, but not so common elsewhere.
+// only remove the thing if it's a symlink into a specific folder. This is
+// a very common use-case of npm's, but not so common elsewhere.
-module.exports = gentlyRm
+exports = module.exports = gentlyRm
-var npm = require('../npm.js')
-var log = require('npmlog')
var resolve = require('path').resolve
var dirname = require('path').dirname
+var normalize = require('path').normalize
+var validate = require('aproba')
+var log = require('npmlog')
var lstat = require('graceful-fs').lstat
var readlink = require('graceful-fs').readlink
var isInside = require('path-is-inside')
var vacuum = require('fs-vacuum')
-var some = require('async-some')
+var chain = require('slide').chain
var asyncMap = require('slide').asyncMap
-var normalize = require('path').normalize
var readCmdShim = require('read-cmd-shim')
var iferr = require('iferr')
+var npm = require('../npm.js')
function gentlyRm (target, gently, base, cb) {
if (!cb) {
@@ -37,8 +38,9 @@ function gentlyRm (target, gently, base, cb) {
// never rm the root, prefix, or bin dirs
//
- // globals included because of `npm link` -- as far as the package requesting
- // the link is concerned, the linked package is always installed globally
+ // globals included because of `npm link` -- as far as the package
+ // requesting the link is concerned, the linked package is always
+ // installed globally
var prefixes = [
npm.prefix,
npm.globalPrefix,
@@ -49,153 +51,234 @@ function gentlyRm (target, gently, base, cb) {
npm.globalBin
]
- var resolved = normalize(resolve(npm.prefix, target))
- if (prefixes.indexOf(resolved) !== -1) {
- log.verbose('gentlyRm', resolved, "is part of npm and can't be removed")
- return cb(new Error('May not delete: ' + resolved))
+ var targetPath = normalize(resolve(npm.prefix, target))
+ if (prefixes.indexOf(targetPath) !== -1) {
+ log.verbose('gentlyRm', targetPath, "is part of npm and can't be removed")
+ return cb(new Error('May not delete: ' + targetPath))
}
+ var options = { log: log.silly.bind(log, 'vacuum-fs') }
+ if (npm.config.get('force') || !gently) options.purge = true
+ if (base) options.base = normalize(resolve(npm.prefix, base))
- follow(resolved, function (realpath) {
- var options = { log: log.silly.bind(log, 'vacuum-fs') }
- if (npm.config.get('force') || !gently) options.purge = true
- if (base) options.base = normalize(resolve(npm.prefix, base))
+ if (!gently) {
+ log.verbose('gentlyRm', "don't care about contents; nuking", targetPath)
+ return vacuum(targetPath, options, cb)
+ }
- if (!gently) {
- log.verbose('gentlyRm', "don't care about contents; nuking", resolved)
- return vacuum(resolved, options, cb)
+ var parent = options.base = options.base || normalize(npm.prefix)
+
+ // Do all the async work we'll need to do in order to tell if this is a
+ // safe operation
+ chain([
+ [isEverInside, parent, prefixes],
+ [readLinkOrShim, targetPath],
+ [isEverInside, targetPath, prefixes],
+ [isEverInside, targetPath, [parent]]
+ ], function (er, results) {
+ if (er) {
+ if (er.code === 'ENOENT') return cb()
+ return cb(er)
+ }
+ var parentInfo = {
+ path: parent,
+ managed: results[0]
+ }
+ var targetInfo = {
+ path: targetPath,
+ symlink: results[1],
+ managed: results[2],
+ inParent: results[3]
}
- var parent = options.base = normalize(base ? resolve(npm.prefix, base) : npm.prefix)
-
- // is the parent directory managed by npm?
- log.silly('gentlyRm', 'verifying', parent, 'is an npm working directory')
- some(prefixes, isManaged(parent), function (er, matched) {
- if (er) return cb(er)
+ isSafeToRm(parentInfo, targetInfo, iferr(cb, thenRemove))
- if (!matched) {
- log.error('gentlyRm', 'containing path', parent, "isn't under npm's control")
- return clobberFail(resolved, parent, cb)
- }
- log.silly('gentlyRm', 'containing path', parent, "is under npm's control, in", matched)
-
- // is the target directly contained within the (now known to be
- // managed) parent?
- if (isInside(resolved, parent)) {
- log.silly('gentlyRm', 'deletion target', resolved, 'is under', parent)
- log.verbose('gentlyRm', 'vacuuming from', resolved, 'up to', parent)
- options.base = parent
- return vacuum(resolved, options, cb)
- }
- log.silly('gentlyRm', realpath, 'is not under', parent)
-
- // the target isn't directly within the parent, but is it itself managed?
- log.silly('gentlyRm', 'verifying', realpath, 'is an npm working directory')
- some(prefixes, isManaged(realpath), function (er, matched) {
- if (er) return cb(er)
-
- if (matched) {
- log.silly('gentlyRm', resolved, "is under npm's control, in", matched)
- if (isInside(realpath, parent)) {
- log.silly('gentlyRm', realpath, 'is controlled by', parent)
- options.base = matched
- log.verbose('gentlyRm', 'removing', resolved, 'with base', options.base)
- return vacuum(resolved, options, cb)
- } else if (resolved !== realpath) {
- log.warn('gentlyRm', 'not removing', resolved, "as it wasn't installed by", parent)
- return cb()
- }
- }
- log.verbose('gentlyRm', resolved, "is not under npm's control")
-
- // the target isn't managed directly, but maybe it's a link...
- log.silly('gentlyRm', 'checking to see if', resolved, 'is a link')
- readLinkOrShim(resolved, function (er, link) {
- if (er) {
- // race conditions are common when unbuilding
- if (er.code === 'ENOENT') return cb(null)
- return cb(er)
- }
-
- if (!link) {
- log.error('gentlyRm', resolved, 'is outside', parent, 'and not a link')
- return clobberFail(resolved, parent, cb)
- }
-
- // ...and maybe the link source, when read...
- log.silly('gentlyRm', resolved, 'is a link')
- // ...is inside the managed parent
- var source = resolve(dirname(resolved), link)
- if (isInside(source, parent)) {
- log.silly('gentlyRm', source, 'symlink target', resolved, 'is inside', parent)
- log.verbose('gentlyRm', 'vacuuming', resolved)
- return vacuum(resolved, options, cb)
- }
-
- log.error('gentlyRm', source, 'symlink target', resolved, 'is not controlled by npm', parent)
- return clobberFail(target, parent, cb)
- })
- })
- })
+ function thenRemove (toRemove, removeBase) {
+ if (!toRemove) return cb()
+ if (removeBase) options.base = removeBase
+ log.verbose('gentlyRm', options.purge ? 'Purging' : 'Vacuuming',
+ toRemove, 'up to', options.base)
+ return vacuum(toRemove, options, cb)
+ }
})
}
-var resolvedPaths = {}
-function isManaged (target) {
- return function predicate (path, cb) {
- if (!path) {
- log.verbose('isManaged', 'no path passed for target', target)
- return cb(null, false)
+exports._isSafeToRm = isSafeToRm
+function isSafeToRm (parent, target, cb) {
+ log.silly('gentlyRm', 'parent.path =', parent.path)
+ log.silly('gentlyRm', 'parent.managed =',
+ parent.managed && parent.managed.target + ' is in ' + parent.managed.path)
+ log.silly('gentlyRm', 'target.path = ', target.path)
+ log.silly('gentlyRm', 'target.symlink =', target.symlink)
+ log.silly('gentlyRm', 'target.managed =',
+ target.managed && target.managed.target + ' is in ' + target.managed.path)
+ log.silly('gentlyRm', 'target.inParent = ', target.inParent)
+
+ // The parent directory or something it symlinks to must eventually be in
+ // a folder that npm maintains.
+ if (!parent.managed) {
+ log.verbose('gentlyRm', parent.path,
+ 'is not contained in any diretory npm is known to control or ' +
+ 'any place they link to')
+ return cb(clobberFail(target.path, 'containing path ' + parent.path +
+ " isn't under npm's control"))
+ }
+
+ // The target or something it symlinks to must eventually be in the parent
+ // or something the parent symlinks to
+ if (target.inParent) {
+ var actualTarget = target.inParent.target
+ var targetsParent = target.inParent.path
+ // if the target.path was what we found in some version of parent, remove
+ // using that parent as the base
+ if (target.path === actualTarget) {
+ return cb(null, target.path, targetsParent)
+ } else {
+ // If something the target.path links to was what was found, just
+ // remove target.path in the location it was found.
+ return cb(null, target.path, dirname(target.path))
}
+ }
- asyncMap([path, target], resolveSymlink, function (er, results) {
- if (er) {
- if (er.code === 'ENOENT') return cb(null, false)
+ // If the target is in a managed directory and is in a symlink, but was
+ // not in our parent that usually means someone else installed a bin file
+ // with the same name as one of our bin files.
+ if (target.managed && target.symlink) {
+ log.warn('gentlyRm', 'not removing', target.path,
+ "as it wasn't installed by", parent.path)
+ return cb()
+ }
- return cb(er)
- }
+ if (target.symlink) {
+ return cb(clobberFail(target.path, target.symlink +
+ ' symlink target is not controlled by npm ' + parent.path))
+ } else {
+ return cb(clobberFail(target.path, 'is outside ' + parent.path +
+ ' and not a link'))
+ }
+}
- var path = results[0]
- var target = results[1]
- var inside = isInside(target, path)
- if (!inside) log.silly('isManaged', target, 'is not inside', path)
+function clobberFail (target, msg) {
+ validate('SS', arguments)
+ var er = new Error('Refusing to delete ' + target + ': ' + msg)
+ er.code = 'EEXIST'
+ er.path = target
+ return er
+}
- return cb(null, inside && path)
- })
- }
+function isENOENT (err) {
+ return err && err.code === 'ENOENT'
+}
- function resolveSymlink (toResolve, cb) {
- var resolved = resolve(npm.prefix, toResolve)
+function notENOENT (err) {
+ return !isENOENT(err)
+}
- // if the path has already been memoized, return immediately
- var cached = resolvedPaths[resolved]
- if (cached) return cb(null, cached)
+function skipENOENT (cb) {
+ return function (err, value) {
+ if (isENOENT(err)) {
+ return cb(null, false)
+ } else {
+ return cb(err, value)
+ }
+ }
+}
- // otherwise, check the path
- readLinkOrShim(resolved, function (er, source) {
- if (er) return cb(er)
+function errorsToValues (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments)
+ var cb = args.pop()
+ args.push(function (err, value) {
+ if (err) {
+ return cb(null, err)
+ } else {
+ return cb(null, value)
+ }
+ })
+ fn.apply(null, args)
+ }
+}
- // if it's not a link, cache & return the path itself
- if (!source) {
- resolvedPaths[resolved] = resolved
- return cb(null, resolved)
+function isNotError (value) {
+ return !(value instanceof Error)
+}
+
+exports._isEverInside = isEverInside
+// return the first of path, where target (or anything it symlinks to)
+// isInside the path (or anything it symlinks to)
+function isEverInside (target, paths, cb) {
+ validate('SAF', arguments)
+ asyncMap(paths, errorsToValues(readAllLinks), iferr(cb, function (resolvedPaths) {
+ var errorFree = resolvedPaths.filter(isNotError)
+ if (errorFree.length === 0) {
+ var badErrors = resolvedPaths.filter(notENOENT)
+ if (badErrors.length === 0) {
+ return cb(null, false)
+ } else {
+ return cb(badErrors[0])
}
+ }
+ readAllLinks(target, iferr(skipENOENT(cb), function (targets) {
+ cb(null, areAnyInsideAny(targets, errorFree))
+ }))
+ }))
+}
- // otherwise, cache & return the link's source
- resolved = resolve(resolved, source)
- resolvedPaths[resolved] = resolved
- cb(null, resolved)
+exports._areAnyInsideAny = areAnyInsideAny
+// Return the first path found that any target is inside
+function areAnyInsideAny (targets, paths) {
+ validate('AA', arguments)
+ var toCheck = []
+ paths.forEach(function (path) {
+ targets.forEach(function (target) {
+ toCheck.push([target, path])
})
+ })
+ for (var ii = 0; ii < toCheck.length; ++ii) {
+ var target = toCheck[ii][0]
+ var path = toCheck[ii][1]
+ var inside = isInside(target, path)
+ if (!inside) log.silly('isEverInside', target, 'is not inside', path)
+ if (inside && path) return inside && path && {target: target, path: path}
}
+ return false
}
-function clobberFail (target, root, cb) {
- var er = new Error('Refusing to delete: ' + target + ' not in ' + root)
- er.code = 'EEXIST'
- er.path = target
- return cb(er)
+exports._readAllLinks = readAllLinks
+// resolves chains of symlinks of unlimited depth, returning a list of paths
+// it's seen in the process when it hits either a symlink cycle or a
+// non-symlink
+function readAllLinks (path, cb) {
+ validate('SF', arguments)
+ var seen = {}
+ _readAllLinks(path)
+
+ function _readAllLinks (path) {
+ if (seen[path]) return cb(null, Object.keys(seen))
+ seen[path] = true
+ resolveSymlink(path, iferr(cb, _readAllLinks))
+ }
}
+exports._resolveSymlink = resolveSymlink
+var resolvedPaths = {}
+function resolveSymlink (symlink, cb) {
+ validate('SF', arguments)
+ var cached = resolvedPaths[symlink]
+ if (cached) return cb(null, cached)
+
+ readLinkOrShim(symlink, iferr(cb, function (symlinkTarget) {
+ if (symlinkTarget) {
+ resolvedPaths[symlink] = resolve(dirname(symlink), symlinkTarget)
+ } else {
+ resolvedPaths[symlink] = symlink
+ }
+ return cb(null, resolvedPaths[symlink])
+ }))
+}
+
+exports._readLinkOrShim = readLinkOrShim
function readLinkOrShim (path, cb) {
+ validate('SF', arguments)
lstat(path, iferr(cb, function (stat) {
if (stat.isSymbolicLink()) {
readlink(path, cb)
@@ -212,10 +295,3 @@ function readLinkOrShim (path, cb) {
}
}))
}
-
-function follow (path, cb) {
- readLinkOrShim(path, function (er, source) {
- if (!source) return cb(path)
- cb(normalize(resolve(dirname(path), source)))
- })
-}
diff --git a/deps/npm/lib/utils/lifecycle.js b/deps/npm/lib/utils/lifecycle.js
index e2ec37c241..7c93cdfada 100644
--- a/deps/npm/lib/utils/lifecycle.js
+++ b/deps/npm/lib/utils/lifecycle.js
@@ -243,6 +243,11 @@ function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {
if (er.code !== 'EPERM') {
er.code = 'ELIFECYCLE'
}
+ fs.stat(npm.dir, function (statError, d) {
+ if (statError && statError.code === 'ENOENT' && npm.dir.split(path.sep).slice(-1)[0] === 'node_modules') {
+ log.warn('', 'Local package.json exists, but node_modules missing, did you mean to install?')
+ }
+ })
er.pkgid = pkg._id
er.stage = stage
er.script = cmd
diff --git a/deps/npm/lib/utils/rename.js b/deps/npm/lib/utils/rename.js
new file mode 100644
index 0000000000..8a44428984
--- /dev/null
+++ b/deps/npm/lib/utils/rename.js
@@ -0,0 +1,16 @@
+'use strict'
+var fs = require('graceful-fs')
+var SaveStack = require('./save-stack.js')
+
+module.exports = rename
+
+function rename (from, to, cb) {
+ var saved = new SaveStack(rename)
+ fs.rename(from, to, function (er) {
+ if (er) {
+ return cb(saved.completeWith(er))
+ } else {
+ return cb()
+ }
+ })
+}
diff --git a/deps/npm/lib/utils/save-stack.js b/deps/npm/lib/utils/save-stack.js
new file mode 100644
index 0000000000..42c4aab5f9
--- /dev/null
+++ b/deps/npm/lib/utils/save-stack.js
@@ -0,0 +1,16 @@
+'use strict'
+var inherits = require('inherits')
+
+module.exports = SaveStack
+
+function SaveStack (fn) {
+ Error.call(this)
+ Error.captureStackTrace(this, fn || SaveStack)
+}
+inherits(SaveStack, Error)
+
+SaveStack.prototype.completeWith = function (er) {
+ this['__' + 'proto' + '__'] = er
+ this.stack = this.stack + '\n\n' + er.stack
+ return this
+}