diff options
Diffstat (limited to 'deps/npm/lib')
24 files changed, 411 insertions, 341 deletions
diff --git a/deps/npm/lib/config/cmd-list.js b/deps/npm/lib/config/cmd-list.js index a1abe80fb0..c54d105247 100644 --- a/deps/npm/lib/config/cmd-list.js +++ b/deps/npm/lib/config/cmd-list.js @@ -37,6 +37,7 @@ var affordances = { 'info': 'view', 'show': 'view', 'find': 'search', + 'add': 'install', 'unlink': 'uninstall', 'remove': 'uninstall', 'rm': 'uninstall', diff --git a/deps/npm/lib/config/defaults.js b/deps/npm/lib/config/defaults.js index 517d82ae1e..da019ac4d6 100644 --- a/deps/npm/lib/config/defaults.js +++ b/deps/npm/lib/config/defaults.js @@ -178,6 +178,7 @@ Object.defineProperty(exports, 'defaults', {get: function () { 'onload-script': false, only: null, optional: true, + 'package-lock': true, parseable: false, 'prefer-offline': false, 'prefer-online': false, @@ -200,6 +201,7 @@ Object.defineProperty(exports, 'defaults', {get: function () { 'save-exact': false, 'save-optional': false, 'save-prefix': '^', + 'save-prod': false, scope: '', 'scripts-prepend-node-path': 'warn-only', searchopts: '', @@ -304,6 +306,7 @@ exports.types = { 'onload-script': [null, String], only: [null, 'dev', 'development', 'prod', 'production'], optional: Boolean, + 'package-lock': Boolean, parseable: Boolean, 'prefer-offline': Boolean, 'prefer-online': Boolean, @@ -321,6 +324,7 @@ exports.types = { 'save-exact': Boolean, 'save-optional': Boolean, 'save-prefix': String, + 'save-prod': Boolean, scope: String, 'scripts-prepend-node-path': [false, true, 'auto', 'warn-only'], searchopts: String, @@ -404,6 +408,7 @@ exports.shorthands = { D: ['--save-dev'], E: ['--save-exact'], O: ['--save-optional'], + P: ['--save-prod'], y: ['--yes'], n: ['--no-yes'], B: ['--save-bundle'], diff --git a/deps/npm/lib/config/pacote.js b/deps/npm/lib/config/pacote.js index 13b7b53f52..705544fe3c 100644 --- a/deps/npm/lib/config/pacote.js +++ b/deps/npm/lib/config/pacote.js @@ -1,25 +1,24 @@ 'use strict' -const BB = require('bluebird') +const Buffer = require('safe-buffer').Buffer -const cp = require('child_process') const npm = require('../npm') const log = require('npmlog') -const packToStream = require('../utils/tar').packToStream +let pack const path = require('path') -const pipe = BB.promisify(require('mississippi').pipe) -const readJson = BB.promisify(require('read-package-json')) -const PassThrough = require('stream').PassThrough let effectiveOwner module.exports = pacoteOpts function pacoteOpts (moreOpts) { + if (!pack) { + pack = require('../pack.js') + } const ownerStats = calculateOwner() const opts = { cache: path.join(npm.config.get('cache'), '_cacache'), defaultTag: npm.config.get('tag'), - dirPacker: prepareAndPack, + dirPacker: pack.packGitDep, hashAlgorithm: 'sha1', localAddress: npm.config.get('local-address'), log: log, @@ -44,17 +43,34 @@ function pacoteOpts (moreOpts) { } if (ownerStats.uid || ownerStats.gid) { - Object.assign(opts, ownerStats, { - cacheUid: ownerStats.uid, - cacheGid: ownerStats.gid - }) + Object.assign(opts, ownerStats) } npm.config.keys.forEach(function (k) { - if (k[0] === '/' && k.match(/.*:_authToken$/)) { + const authMatch = k[0] === '/' && k.match( + /(.*):(_authToken|username|_password|password|email|always-auth)$/ + ) + if (authMatch) { + const nerfDart = authMatch[1] + const key = authMatch[2] + const val = npm.config.get(k) if (!opts.auth) { opts.auth = {} } - opts.auth[k.replace(/:_authToken$/, '')] = { - token: npm.config.get(k) + if (!opts.auth[nerfDart]) { + opts.auth[nerfDart] = { + alwaysAuth: !!npm.config.get('always-auth') + } + } + if (key === '_authToken') { + opts.auth[nerfDart].token = val + } else if (key.match(/password$/i)) { + opts.auth[nerfDart].password = + // the config file stores password auth already-encoded. pacote expects + // the actual username/password pair. + Buffer.from(val, 'base64').toString('utf8') + } else if (key === 'always-auth') { + opts.auth[nerfDart].alwaysAuth = val === 'false' ? false : !!val + } else { + opts.auth[nerfDart][key] = val } } if (k[0] === '@') { @@ -90,86 +106,3 @@ function calculateOwner () { return effectiveOwner } - -const PASSTHROUGH_OPTS = [ - 'always-auth', - 'auth-type', - 'ca', - 'cafile', - 'cert', - 'git', - 'local-address', - 'maxsockets', - 'offline', - 'prefer-offline', - 'prefer-online', - 'proxy', - 'https-proxy', - 'registry', - 'send-metrics', - 'sso-poll-frequency', - 'sso-type', - 'strict-ssl' -] - -function prepareAndPack (manifest, dir) { - const stream = new PassThrough() - readJson(path.join(dir, 'package.json')).then((pkg) => { - if (pkg.scripts && pkg.scripts.prepare) { - log.verbose('prepareGitDep', `${manifest._spec}: installing devDeps and running prepare script.`) - const cliArgs = PASSTHROUGH_OPTS.reduce((acc, opt) => { - if (npm.config.get(opt, 'cli') != null) { - acc.push(`--${opt}=${npm.config.get(opt)}`) - } - return acc - }, []) - const child = cp.spawn(process.env.NODE || process.execPath, [ - require.main.filename, - 'install', - '--ignore-prepublish', - '--no-progress', - '--no-save' - ].concat(cliArgs), { - cwd: dir, - env: process.env - }) - let errData = [] - let errDataLen = 0 - let outData = [] - let outDataLen = 0 - child.stdout.on('data', (data) => { - outData.push(data) - outDataLen += data.length - log.gauge.pulse('preparing git package') - }) - child.stderr.on('data', (data) => { - errData.push(data) - errDataLen += data.length - log.gauge.pulse('preparing git package') - }) - return BB.fromNode((cb) => { - child.on('error', cb) - child.on('exit', (code, signal) => { - if (code > 0) { - const err = new Error(`${signal}: npm exited with code ${code} while attempting to build ${manifest._requested}. Clone the repository manually and run 'npm install' in it for more information.`) - err.code = code - err.signal = signal - cb(err) - } else { - cb() - } - }) - }).then(() => { - if (outDataLen > 0) log.silly('prepareGitDep', '1>', Buffer.concat(outData, outDataLen).toString()) - if (errDataLen > 0) log.silly('prepareGitDep', '2>', Buffer.concat(errData, errDataLen).toString()) - }, (err) => { - if (outDataLen > 0) log.error('prepareGitDep', '1>', Buffer.concat(outData, outDataLen).toString()) - if (errDataLen > 0) log.error('prepareGitDep', '2>', Buffer.concat(errData, errDataLen).toString()) - throw err - }) - } - }).then(() => { - return pipe(packToStream(manifest, dir), stream) - }).catch((err) => stream.emit('error', err)) - return stream -} diff --git a/deps/npm/lib/fetch-package-metadata.js b/deps/npm/lib/fetch-package-metadata.js index 45d6acbfae..cca6dc64f4 100644 --- a/deps/npm/lib/fetch-package-metadata.js +++ b/deps/npm/lib/fetch-package-metadata.js @@ -12,7 +12,7 @@ const npmlog = require('npmlog') const limit = require('call-limit') const tempFilename = require('./utils/temp-filename') const pacote = require('pacote') -const pacoteOpts = require('./config/pacote') +let pacoteOpts const isWindows = require('./utils/is-windows.js') function andLogAndFinish (spec, tracker, done) { @@ -52,7 +52,9 @@ function fetchPackageMetadata (spec, where, opts, done) { err.code = 'EWINDOWSPATH' return logAndFinish(err) } - + if (!pacoteOpts) { + pacoteOpts = require('./config/pacote') + } pacote.manifest(dep, pacoteOpts({ annotate: true, fullMetadata: opts.fullMetadata, @@ -83,6 +85,9 @@ function fetchPackageMetadata (spec, where, opts, done) { module.exports.addBundled = addBundled function addBundled (pkg, next) { validate('OF', arguments) + if (!pacoteOpts) { + pacoteOpts = require('./config/pacote') + } if (pkg._bundled !== undefined) return next(null, pkg) if (!pkg.bundleDependencies && pkg._requested.type !== 'directory') return next(null, pkg) diff --git a/deps/npm/lib/install.js b/deps/npm/lib/install.js index c567f624f9..5d111b32c8 100644 --- a/deps/npm/lib/install.js +++ b/deps/npm/lib/install.js @@ -29,7 +29,7 @@ install.usage = usage( '\nnpm install <tarball url>' + '\nnpm install <git:// url>' + '\nnpm install <github username>/<github project>', - '[--save|--save-dev|--save-optional] [--save-exact]' + '[--save-prod|--save-dev|--save-optional] [--save-exact] [--no-save]' ) install.completion = function (opts, cb) { @@ -98,6 +98,7 @@ var path = require('path') // dependencies var log = require('npmlog') var readPackageTree = require('read-package-tree') +var readPackageJson = require('read-package-json') var chain = require('slide').chain var asyncMap = require('slide').asyncMap var archy = require('archy') @@ -137,10 +138,11 @@ var doReverseSerialActions = require('./install/actions.js').doReverseSerial var doParallelActions = require('./install/actions.js').doParallel var doOneAction = require('./install/actions.js').doOne var removeObsoleteDep = require('./install/deps.js').removeObsoleteDep +var removeExtraneous = require('./install/deps.js').removeExtraneous +var computeVersionSpec = require('./install/deps.js').computeVersionSpec var packageId = require('./utils/package-id.js') var moduleName = require('./utils/module-name.js') var errorMessage = require('./utils/error-message.js') -var removeDeps = require('./install/deps.js').removeDeps var isExtraneous = require('./install/is-extraneous.js') function unlockCB (lockPath, name, cb) { @@ -202,6 +204,11 @@ function Installer (where, dryrun, args) { this.where = where this.dryrun = dryrun this.args = args + // fakechildren are children created from the lockfile and lack relationship data + // the only exist when the tree does not match the lockfile + // this is fine when doing full tree installs/updates but not ok when modifying only + // a few deps via `npm install` or `npm uninstall`. + this.fakeChildren = true this.currentTree = null this.idealTree = null this.differences = [] @@ -245,6 +252,11 @@ Installer.prototype.run = function (_cb) { var installSteps = [] var postInstallSteps = [] + if (!this.dryrun) { + installSteps.push( + [this.newTracker(log, 'runTopLevelLifecycles', 2)], + [this, this.runPreinstallTopLevelLifecycles]) + } installSteps.push( [this.newTracker(log, 'loadCurrentTree', 4)], [this, this.loadCurrentTree], @@ -265,9 +277,6 @@ 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']) @@ -313,9 +322,9 @@ Installer.prototype.run = function (_cb) { } Installer.prototype.loadArgMetadata = function (next) { - var self = this - getAllMetadata(this.args, this.currentTree, process.cwd(), iferr(next, function (args) { - self.args = args + getAllMetadata(this.args, this.currentTree, process.cwd(), iferr(next, (args) => { + this.args = args + if (args.length) this.fakeChildren = false next() })) } @@ -354,6 +363,14 @@ var flatNameFromTree = require('./install/flatten-tree.js').flatNameFromTree Installer.prototype.normalizeCurrentTree = function (cb) { this.currentTree.isTop = true normalizeTree(this.currentTree) + // If the user didn't have a package.json then fill in deps with what was on disk + if (this.currentTree.error) { + for (let child of this.currentTree.children) { + if (!child.fakeChild && isExtraneous(child)) { + this.currentTree.package.dependencies[child.package.name] = computeVersionSpec(this.currentTree, child) + } + } + } return cb() function normalizeTree (tree) { @@ -386,9 +403,9 @@ Installer.prototype.loadIdealTree = function (cb) { Installer.prototype.pruneIdealTree = function (cb) { var toPrune = this.idealTree.children - .filter((n) => !n.fromShrinkwrap && isExtraneous(n)) + .filter((n) => !n.fakeChild && isExtraneous(n)) .map((n) => ({name: moduleName(n)})) - return removeDeps(toPrune, this.idealTree, null, log.newGroup('pruneDeps'), cb) + return removeExtraneous(toPrune, this.idealTree, cb) } Installer.prototype.loadAllDepsIntoIdealTree = function (cb) { @@ -400,14 +417,14 @@ Installer.prototype.loadAllDepsIntoIdealTree = function (cb) { var installNewModules = !!this.args.length var steps = [] + const depsToPreload = Object.assign({}, + this.dev ? this.idealTree.package.devDependencies : {}, + this.prod ? this.idealTree.package.dependencies : {} + ) if (installNewModules) { steps.push([validateArgs, this.idealTree, this.args]) steps.push([loadRequestedDeps, this.args, this.idealTree, saveDeps, cg.newGroup('loadRequestedDeps')]) } else { - const depsToPreload = Object.assign({}, - this.dev ? this.idealTree.package.devDependencies : {}, - this.prod ? this.idealTree.package.dependencies : {} - ) if (this.prod || this.dev) { steps.push( [prefetchDeps, this.idealTree, depsToPreload, cg.newGroup('prefetchDeps')]) @@ -549,13 +566,16 @@ Installer.prototype.runPreinstallTopLevelLifecycles = function (cb) { if (this.failing) return cb() 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) + readPackageJson(path.join(this.where, 'package.json'), log, false, (err, data) => { + if (err) return cb() + this.currentTree = createNode({ + isTop: true, + package: data, + path: this.where + }) + doOneAction('preinstall', this.where, this.currentTree, log.newGroup('preinstall:.'), cb) + }) } Installer.prototype.runPostinstallTopLevelLifecycles = function (cb) { @@ -581,7 +601,7 @@ Installer.prototype.saveToDependencies = function (cb) { validate('F', arguments) if (this.failing) return cb() log.silly('install', 'saveToDependencies') - saveRequested(this.args, this.idealTree, cb) + saveRequested(this.idealTree, cb) } Installer.prototype.readGlobalPackageData = function (cb) { @@ -655,7 +675,7 @@ function isLink (child) { Installer.prototype.loadShrinkwrap = function (cb) { validate('F', arguments) log.silly('install', 'loadShrinkwrap') - readShrinkwrap.andInflate(this.idealTree, cb) + readShrinkwrap.andInflate(this.idealTree, {fakeChildren: this.fakeChildren}, cb) } Installer.prototype.getInstalledModules = function () { @@ -693,21 +713,22 @@ Installer.prototype.printInstalled = function (cb) { validate('F', arguments) if (this.failing) return cb() log.silly('install', 'printInstalled') + const diffs = this.differences.concat((this.idealTree.removedChildren || []).map((r) => ['remove', r])) if (npm.config.get('json')) { - return this.printInstalledForJSON(cb) + return this.printInstalledForJSON(diffs, cb) } else if (npm.config.get('parseable')) { - return this.printInstalledForParseable(cb) + return this.printInstalledForParseable(diffs, cb) } else { - return this.printInstalledForHuman(cb) + return this.printInstalledForHuman(diffs, cb) } } -Installer.prototype.printInstalledForHuman = function (cb) { +Installer.prototype.printInstalledForHuman = function (diffs, cb) { var removed = 0 var added = 0 var updated = 0 var moved = 0 - this.differences.forEach(function (action) { + diffs.forEach(function (action) { var mutation = action[0] if (mutation === 'remove') { ++removed @@ -743,7 +764,7 @@ Installer.prototype.printInstalledForHuman = function (cb) { } } -Installer.prototype.printInstalledForJSON = function (cb) { +Installer.prototype.printInstalledForJSON = function (diffs, cb) { var result = { added: [], removed: [], @@ -764,7 +785,7 @@ Installer.prototype.printInstalledForJSON = function (cb) { } result.warnings.push(message) }) - this.differences.forEach(function (action) { + diffs.forEach(function (action) { var mutation = action[0] var child = action[1] var record = recordAction(action) @@ -805,9 +826,9 @@ Installer.prototype.printInstalledForJSON = function (cb) { } } -Installer.prototype.printInstalledForParseable = function (cb) { +Installer.prototype.printInstalledForParseable = function (diffs, cb) { var self = this - this.differences.forEach(function (action) { + diffs.forEach(function (action) { var mutation = action[0] var child = action[1] if (mutation === 'move') { @@ -819,7 +840,7 @@ Installer.prototype.printInstalledForParseable = function (cb) { mutation + '\t' + moduleName(child) + '\t' + (child.package ? child.package.version : '') + '\t' + - path.relative(self.where, child.path) + '\t' + + (child.path ? path.relative(self.where, child.path) : '') + '\t' + (previousVersion || '') + '\t' + (previousPath || '')) }) diff --git a/deps/npm/lib/install/action/extract.js b/deps/npm/lib/install/action/extract.js index 7839177850..437d7e57f7 100644 --- a/deps/npm/lib/install/action/extract.js +++ b/deps/npm/lib/install/action/extract.js @@ -10,22 +10,19 @@ const moduleName = require('../../utils/module-name.js') const moduleStagingPath = require('../module-staging-path.js') const move = BB.promisify(require('../../utils/move.js')) const npa = require('npm-package-arg') -const npm = require('../../npm.js') const packageId = require('../../utils/package-id.js') const pacote = require('pacote') -const pacoteOpts = require('../../config/pacote') +let pacoteOpts const path = require('path') module.exports = extract function extract (staging, pkg, log) { log.silly('extract', packageId(pkg)) - const up = npm.config.get('unsafe-perm') - const user = up ? null : npm.config.get('user') - const group = up ? null : npm.config.get('group') const extractTo = moduleStagingPath(staging, pkg) + if (!pacoteOpts) { + pacoteOpts = require('../../config/pacote') + } const opts = pacoteOpts({ - uid: user, - gid: group, integrity: pkg.package._integrity }) return pacote.extract( diff --git a/deps/npm/lib/install/action/finalize.js b/deps/npm/lib/install/action/finalize.js index 1e86475710..ba38e602f8 100644 --- a/deps/npm/lib/install/action/finalize.js +++ b/deps/npm/lib/install/action/finalize.js @@ -23,10 +23,11 @@ module.exports = function (staging, pkg, log) { const requested = pkg.package._requested || getRequested(pkg) if (requested.type === 'directory') { + const relative = path.relative(path.dirname(pkg.path), pkg.realpath) return makeParentPath(pkg.path) - .then(() => symlink(pkg.realpath, pkg.path, 'junction')) + .then(() => symlink(relative, pkg.path, 'junction')) .catch((ex) => { - return rimraf(pkg.path).then(() => symlink(pkg.realpath, pkg.path, 'junction')) + return rimraf(pkg.path).then(() => symlink(relative, pkg.path, 'junction')) }) } else { return makeParentPath(pkg.realpath) diff --git a/deps/npm/lib/install/action/preinstall.js b/deps/npm/lib/install/action/preinstall.js index a6f85b0a5a..a16082ef73 100644 --- a/deps/npm/lib/install/action/preinstall.js +++ b/deps/npm/lib/install/action/preinstall.js @@ -1,9 +1,8 @@ 'use strict' var lifecycle = require('../../utils/lifecycle.js') var packageId = require('../../utils/package-id.js') -var moduleStagingPath = require('../module-staging-path.js') module.exports = function (staging, pkg, log, next) { log.silly('preinstall', packageId(pkg)) - lifecycle(pkg.package, 'preinstall', moduleStagingPath(staging, pkg), false, false, next) + lifecycle(pkg.package, 'preinstall', pkg.path, false, false, next) } diff --git a/deps/npm/lib/install/action/refresh-package-json.js b/deps/npm/lib/install/action/refresh-package-json.js index 337be0caf2..6910803451 100644 --- a/deps/npm/lib/install/action/refresh-package-json.js +++ b/deps/npm/lib/install/action/refresh-package-json.js @@ -10,13 +10,13 @@ module.exports = function (staging, pkg, log) { return readJson(path.join(pkg.path, 'package.json'), false).then((metadata) => { Object.keys(pkg.package).forEach(function (key) { - if (key !== '_injectedFromShrinkwrap' && !isEmpty(pkg.package[key])) { + if (!isEmpty(pkg.package[key])) { metadata[key] = pkg.package[key] - if (key === '_resolved' && metadata[key] == null && pkg.package._injectedFromShrinkwrap) { - metadata[key] = pkg.package._injectedFromShrinkwrap.resolved - } } }) + if (metadata._resolved == null && pkg.fakeChild) { + metadata._resolved = pkg.fakeChild.resolved + } // These two sneak in and it's awful delete metadata.readme delete metadata.readmeFilename diff --git a/deps/npm/lib/install/deps.js b/deps/npm/lib/install/deps.js index 3f3433535f..c0fe905d4b 100644 --- a/deps/npm/lib/install/deps.js +++ b/deps/npm/lib/install/deps.js @@ -183,7 +183,9 @@ function packageRelativePath (tree) { if (!tree) return '' var requested = tree.package._requested || {} var isLocal = requested.type === 'directory' || requested.type === 'file' - return isLocal ? requested.fetchSpec : tree.path + return isLocal ? requested.fetchSpec + : (tree.isLink || tree.isInLink) && !preserveSymlinks() ? tree.realpath + : tree.path } function matchingDep (tree, name) { @@ -227,14 +229,24 @@ exports.loadRequestedDeps = function (args, tree, saveToDependencies, log, next) } var childName = moduleName(child) child.saveSpec = computeVersionSpec(tree, child) - if (saveToDependencies) { - tree.package[getSaveType(tree, child)][childName] = child.saveSpec - } - if (getSaveType(tree, child) === 'optionalDependencies') { - tree.package.dependencies[childName] = child.saveSpec - } child.userRequired = true - child.save = saveToDependencies + child.save = getSaveType(tree, child) + const types = ['dependencies', 'devDependencies', 'optionalDependencies'] + if (child.save) { + tree.package[child.save][childName] = child.saveSpec + // Astute readers might notice that this exact same code exists in + // save.js under a different guise. That code is responsible for deps + // being removed from the final written `package.json`. The removal in + // this function is specifically to prevent "installed as both X and Y" + // warnings when moving an existing dep between different dep fields. + // + // Or, try it by removing this loop, and do `npm i -P x && npm i -D x` + for (let saveType of types) { + if (child.save !== saveType) { + delete tree.package[saveType][childName] + } + } + } // For things the user asked to install, that aren't a dependency (or // won't be when we're done), flag it as "depending" on the user @@ -246,9 +258,17 @@ exports.loadRequestedDeps = function (args, tree, saveToDependencies, log, next) }, andForEachChild(loadDeps, andFinishTracker(log, next))) } +module.exports.computeVersionSpec = computeVersionSpec function computeVersionSpec (tree, child) { validate('OO', arguments) - var requested = child.package._requested + var requested + if (child.package._requested) { + requested = child.package._requested + } else if (child.package._from) { + requested = npa(child.package._from) + } else { + requested = npa.resolve(child.package.name, child.package.version) + } if (requested.registry) { var version = child.package.version var rangeDescriptor = '' @@ -275,26 +295,38 @@ function noModuleNameMatches (name) { // while this implementation does not require async calling, doing so // gives this a consistent interface with loadDeps et al -exports.removeDeps = function (args, tree, saveToDependencies, log, next) { - validate('AOOF', [args, tree, log, next]) - args.forEach(function (pkg) { +exports.removeDeps = function (args, tree, saveToDependencies, next) { + validate('AOSF|AOZF', [args, tree, saveToDependencies, next]) + for (let pkg of args) { var pkgName = moduleName(pkg) var toRemove = tree.children.filter(moduleNameMatches(pkgName)) var pkgToRemove = toRemove[0] || createChild({package: {name: pkgName}}) - if (tree.isTop) { - if (saveToDependencies) { - pkgToRemove.save = getSaveType(tree, pkg) - delete tree.package[pkgToRemove.save][pkgName] - if (pkgToRemove.save === 'optionalDependencies') { - delete tree.package.dependencies[pkgName] - } - replaceModuleByPath(tree, 'removed', pkgToRemove) + var saveType = getSaveType(tree, pkg) || 'dependencies' + if (tree.isTop && saveToDependencies) { + pkgToRemove.save = saveType + } + if (tree.package[saveType][pkgName]) { + delete tree.package[saveType][pkgName] + if (saveType === 'optionalDependencies' && tree.package.dependencies[pkgName]) { + delete tree.package.dependencies[pkgName] } - pkgToRemove.requiredBy = pkgToRemove.requiredBy.filter((parent) => parent !== tree) } - if (pkgToRemove.requiredBy.length === 0) removeObsoleteDep(pkgToRemove) - }) - log.finish() + replaceModuleByPath(tree, 'removedChildren', pkgToRemove) + for (let parent of pkgToRemove.requiredBy) { + parent.requires = parent.requires.filter((child) => child !== pkgToRemove) + } + pkgToRemove.requiredBy = pkgToRemove.requiredBy.filter((parent) => parent !== tree) + } + next() +} +exports.removeExtraneous = function (args, tree, next) { + for (let pkg of args) { + var pkgName = moduleName(pkg) + var toRemove = tree.children.filter(moduleNameMatches(pkgName)) + if (toRemove.length) { + removeObsoleteDep(toRemove[0]) + } + } next() } @@ -639,6 +671,13 @@ var findRequirement = exports.findRequirement = function (tree, name, requested, return findRequirement(tree.parent, name, requested, requestor) } +function preserveSymlinks () { + if (!('NODE_PRESERVE_SYMLINKS' in process.env)) return false + const value = process.env.NODE_PRESERVE_SYMLINKS + if (value == null || value === '' || value === 'false' || value === 'no' || value === '0') return false + return true +} + // Find the highest level in the tree that we can install this module in. // If the module isn't installed above us yet, that'd be the very top. // If it is, then it's the level below where its installed. @@ -670,7 +709,7 @@ var earliestInstallable = exports.earliestInstallable = function (requiredBy, tr var devDeps = tree.package.devDependencies || {} if (tree.isTop && devDeps[pkg.name]) { - var requested = npa.resolve(pkg.name, devDeps[pkg.name], tree.path) + var requested = childDependencySpecifier(tree, pkg.name, devDeps[pkg.name]) if (!doesChildVersionMatch({package: pkg}, requested, tree)) { return null } @@ -684,7 +723,7 @@ var earliestInstallable = exports.earliestInstallable = function (requiredBy, tr if (npm.config.get('global-style') && tree.parent.isTop) return tree if (npm.config.get('legacy-bundling')) return tree - if (!process.env.NODE_PRESERVE_SYMLINKS && /^[.][.][\\/]/.test(path.relative(tree.parent.realpath, tree.realpath))) return tree + if (!preserveSymlinks() && /^[.][.][\\/]/.test(path.relative(tree.parent.realpath, tree.realpath))) return tree return (earliestInstallable(requiredBy, tree.parent, pkg) || tree) } diff --git a/deps/npm/lib/install/inflate-shrinkwrap.js b/deps/npm/lib/install/inflate-shrinkwrap.js index 9878b0f19a..8cb75626bb 100644 --- a/deps/npm/lib/install/inflate-shrinkwrap.js +++ b/deps/npm/lib/install/inflate-shrinkwrap.js @@ -2,10 +2,10 @@ const BB = require('bluebird') -const addBundled = BB.promisify(require('../fetch-package-metadata.js').addBundled) +let addBundled const childPath = require('../utils/child-path.js') const createChild = require('./node.js').create -const fetchPackageMetadata = BB.promisify(require('../fetch-package-metadata.js')) +let fetchPackageMetadata const inflateBundled = require('./inflate-bundled.js') const moduleName = require('../utils/module-name.js') const normalizePackageData = require('normalize-package-data') @@ -14,17 +14,28 @@ const realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier.js') const validate = require('aproba') const path = require('path') -module.exports = function (tree, swdeps, finishInflating) { - if (!npm.config.get('shrinkwrap')) return finishInflating() +module.exports = function (tree, swdeps, opts, finishInflating) { + if (!fetchPackageMetadata) { + fetchPackageMetadata = BB.promisify(require('../fetch-package-metadata.js')) + addBundled = BB.promisify(fetchPackageMetadata.addBundled) + } + if (!npm.config.get('shrinkwrap') || !npm.config.get('package-lock')) { + return finishInflating() + } + if (arguments.length === 3) { + finishInflating = opts + opts = {} + } tree.loaded = true - return inflateShrinkwrap(tree.path, tree, swdeps).then( + return inflateShrinkwrap(tree.path, tree, swdeps, opts).then( () => finishInflating(), finishInflating ) } -function inflateShrinkwrap (topPath, tree, swdeps) { - validate('SOO', arguments) +function inflateShrinkwrap (topPath, tree, swdeps, opts) { + validate('SOO|SOOO', arguments) + if (!opts) opts = {} const onDisk = {} tree.children.forEach((child) => { onDisk[moduleName(child)] = child @@ -43,7 +54,7 @@ function inflateShrinkwrap (topPath, tree, swdeps) { const dependencies = sw.dependencies || {} const requested = realizeShrinkwrapSpecifier(name, sw, topPath) return inflatableChild( - onDisk[name], name, topPath, tree, sw, requested + onDisk[name], name, topPath, tree, sw, requested, opts ).then((child) => { return inflateShrinkwrap(topPath, child, dependencies) }) @@ -58,8 +69,8 @@ function normalizePackageDataNoErrors (pkg) { } } -function inflatableChild (onDiskChild, name, topPath, tree, sw, requested) { - validate('OSSOOO|ZSSOOO', arguments) +function inflatableChild (onDiskChild, name, topPath, tree, sw, requested, opts) { + validate('OSSOOOO|ZSSOOOO', arguments) if (onDiskChild && childIsEquivalent(sw, requested, onDiskChild)) { // The version on disk matches the shrinkwrap entry. if (!onDiskChild.fromShrinkwrap) onDiskChild.fromShrinkwrap = true @@ -77,7 +88,7 @@ function inflatableChild (onDiskChild, name, topPath, tree, sw, requested) { normalizePackageDataNoErrors(onDiskChild.package) tree.children.push(onDiskChild) return BB.resolve(onDiskChild) - } else if (sw.version && sw.integrity) { + } else if (opts.fakeChildren !== false && sw.version && sw.integrity) { // The shrinkwrap entry has an integrity field. We can fake a pkg to get // the installer to do a content-address fetch from the cache, if possible. return BB.resolve(makeFakeChild(name, topPath, tree, sw, requested)) @@ -101,8 +112,7 @@ function makeFakeChild (name, topPath, tree, sw, requested) { _from: from, _spec: requested.rawSpec, _where: topPath, - _args: [[requested.toString(), topPath]], - _injectedFromShrinkwrap: sw + _args: [[requested.toString(), topPath]] } let bundleAdded = BB.resolve() if (Object.keys(sw.dependencies || {}).some((d) => { @@ -118,6 +128,7 @@ function makeFakeChild (name, topPath, tree, sw, requested) { parent: tree, children: pkg._bundled || [], fromShrinkwrap: true, + fakeChild: sw, fromBundle: sw.bundled ? tree.fromBundle || tree : null, path: childPath(tree.path, pkg), realpath: childPath(tree.realpath, pkg), diff --git a/deps/npm/lib/install/is-extraneous.js b/deps/npm/lib/install/is-extraneous.js index f0d599965f..a6477c2374 100644 --- a/deps/npm/lib/install/is-extraneous.js +++ b/deps/npm/lib/install/is-extraneous.js @@ -6,14 +6,6 @@ function isExtraneous (tree) { return result } -function isNotRequired (tree) { - return tree.requiredBy && tree.requiredBy.length === 0 -} - -function parentHasNoPjson (tree) { - return tree.parent && tree.parent.isTop && tree.parent.error -} - function topHasNoPjson (tree) { var top = tree while (!top.isTop) top = top.parent @@ -24,8 +16,6 @@ function isNotExtraneous (tree, isCycle) { if (!isCycle) isCycle = {} if (tree.isTop || tree.userRequired) { return true - } else if (isNotRequired(tree) && parentHasNoPjson(tree)) { - return true } else if (isCycle[tree.path]) { return topHasNoPjson(tree) } else { diff --git a/deps/npm/lib/install/mutate-into-logical-tree.js b/deps/npm/lib/install/mutate-into-logical-tree.js index 491f20913c..018745cc5f 100644 --- a/deps/npm/lib/install/mutate-into-logical-tree.js +++ b/deps/npm/lib/install/mutate-into-logical-tree.js @@ -7,6 +7,7 @@ var isExtraneous = require('./is-extraneous.js') var validateAllPeerDeps = require('./deps.js').validateAllPeerDeps var packageId = require('../utils/package-id.js') var moduleName = require('../utils/module-name.js') +var npm = require('../npm.js') // Return true if tree is a part of a cycle that: // A) Never connects to the top of the tree @@ -128,7 +129,7 @@ function translateTree_ (tree, seen) { pkg.path = tree.path pkg.error = tree.error - pkg.extraneous = isExtraneous(tree) + pkg.extraneous = !tree.isTop && (!tree.parent.isTop || !tree.parent.error) && !npm.config.get('global') && isExtraneous(tree) if (tree.target && tree.parent && !tree.parent.target) pkg.link = tree.realpath return pkg } diff --git a/deps/npm/lib/install/read-shrinkwrap.js b/deps/npm/lib/install/read-shrinkwrap.js index 913c303482..de398fb40b 100644 --- a/deps/npm/lib/install/read-shrinkwrap.js +++ b/deps/npm/lib/install/read-shrinkwrap.js @@ -9,7 +9,6 @@ const log = require('npmlog') const parseJSON = require('../utils/parse-json.js') const path = require('path') const PKGLOCK_VERSION = require('../npm.js').lockfileVersion -const pkgSri = require('../utils/package-integrity.js') const readFileAsync = BB.promisify(fs.readFile) @@ -34,14 +33,6 @@ function readShrinkwrap (child, next) { throw ex } } - if ( - pkgJson && - parsed && - parsed.packageIntegrity && - !pkgSri.check(JSON.parse(pkgJson), parsed.packageIntegrity) - ) { - log.info('read-shrinkwrap', `${name} will be updated because package.json does not match what it was generated against.`) - } if (parsed && parsed.lockfileVersion !== PKGLOCK_VERSION) { log.warn('read-shrinkwrap', `This version of npm is compatible with lockfileVersion@${PKGLOCK_VERSION}, but ${name} was generated for lockfileVersion@${parsed.lockfileVersion || 0}. I'll try to do my best with it!`) } @@ -56,10 +47,14 @@ function maybeReadFile (name, child) { ).catch({code: 'ENOENT'}, () => null) } -module.exports.andInflate = function (child, next) { +module.exports.andInflate = function (child, opts, next) { + if (arguments.length === 2) { + next = opts + opts = {} + } readShrinkwrap(child, iferr(next, function () { if (child.package._shrinkwrap) { - return inflateShrinkwrap(child, child.package._shrinkwrap.dependencies || {}, next) + return inflateShrinkwrap(child, child.package._shrinkwrap.dependencies || {}, opts, next) } else { return next() } diff --git a/deps/npm/lib/install/save.js b/deps/npm/lib/install/save.js index 5d5f4e7f7a..56a4a892ad 100644 --- a/deps/npm/lib/install/save.js +++ b/deps/npm/lib/install/save.js @@ -19,9 +19,9 @@ const writeFileAtomic = require('write-file-atomic') // if the -S|--save option is specified, then write installed packages // as dependencies to a package.json file. -exports.saveRequested = function (args, tree, andReturn) { - validate('AOF', arguments) - savePackageJson(args, tree, andWarnErrors(andSaveShrinkwrap(tree, andReturn))) +exports.saveRequested = function (tree, andReturn) { + validate('OF', arguments) + savePackageJson(tree, andWarnErrors(andSaveShrinkwrap(tree, andReturn))) } function andSaveShrinkwrap (tree, andReturn) { @@ -43,13 +43,14 @@ function andWarnErrors (cb) { function saveShrinkwrap (tree, next) { validate('OF', arguments) + if (!npm.config.get('shrinkwrap') || !npm.config.get('package-lock')) { + next() + } createShrinkwrap(tree, {silent: false}, next) } -function savePackageJson (args, tree, next) { - validate('AOF', arguments) - if (!args || !args.length) { return next() } - +function savePackageJson (tree, next) { + validate('OF', arguments) var saveBundle = npm.config.get('save-bundle') // each item in the tree is a top-level thing that should be saved @@ -84,8 +85,23 @@ function savePackageJson (args, tree, next) { }) log.verbose('saving', toSave) + const types = ['dependencies', 'devDependencies', 'optionalDependencies'] toSave.forEach(function (pkg) { tree.package[pkg.save][pkg.name] = pkg.spec + const movedFrom = [] + for (let saveType of types) { + if ( + pkg.save !== saveType && + tree.package[saveType] && + tree.package[saveType][pkg.name] + ) { + movedFrom.push(saveType) + delete tree.package[saveType][pkg.name] + } + } + if (movedFrom.length) { + log.notice('save', `${pkg.name} is being moved from ${movedFrom.join(' and ')} to ${pkg.save}`) + } if (saveBundle) { var ii = bundle.indexOf(pkg.name) if (ii === -1) bundle.push(pkg.name) @@ -116,6 +132,7 @@ exports.getSaveType = function (tree, arg) { var globalInstall = npm.config.get('global') var noSaveFlags = !npm.config.get('save') && !npm.config.get('save-dev') && + !npm.config.get('save-prod') && !npm.config.get('save-optional') if (globalInstall || noSaveFlags) return null @@ -123,6 +140,8 @@ exports.getSaveType = function (tree, arg) { return 'optionalDependencies' } else if (npm.config.get('save-dev')) { return 'devDependencies' + } else if (npm.config.get('save-prod')) { + return 'dependencies' } else { if (arg) { var name = moduleName(arg) @@ -152,8 +171,8 @@ function getThingsToSave (tree) { function getThingsToRemove (tree) { validate('O', arguments) - if (!tree.removed) return [] - var toRemove = tree.removed.map(function (child) { + if (!tree.removedChildren) return [] + var toRemove = tree.removedChildren.map(function (child) { return { name: moduleName(child), save: child.save diff --git a/deps/npm/lib/pack.js b/deps/npm/lib/pack.js index 68c6030ee8..075a672d66 100644 --- a/deps/npm/lib/pack.js +++ b/deps/npm/lib/pack.js @@ -8,16 +8,19 @@ const BB = require('bluebird') const cache = require('./cache') const cacache = require('cacache') +const cp = require('child_process') const deprCheck = require('./utils/depr-check') -const fpm = BB.promisify(require('./fetch-package-metadata')) +const fpm = require('./fetch-package-metadata') const fs = require('graceful-fs') const install = require('./install') const lifecycle = BB.promisify(require('./utils/lifecycle')) +const log = require('npmlog') const move = require('move-concurrently') const npm = require('./npm') const output = require('./utils/output') const pacoteOpts = require('./config/pacote') const path = require('path') +const PassThrough = require('stream').PassThrough const pathIsInside = require('path-is-inside') const pipe = BB.promisify(require('mississippi').pipe) const prepublishWarning = require('./utils/warn-deprecated')('prepublish-on-install') @@ -53,7 +56,7 @@ function pack (args, silent, cb) { // add to cache, then cp to the cwd function pack_ (pkg, dir) { - return fpm(pkg, dir).then((mani) => { + return BB.fromNode((cb) => fpm(pkg, dir, cb)).then((mani) => { let name = mani.name[0] === '@' // scoped packages get special treatment ? mani.name.substr(1).replace(/\//g, '-') @@ -108,10 +111,111 @@ function prepareDirectory (dir) { module.exports.packDirectory = packDirectory function packDirectory (mani, dir, target) { deprCheck(mani) - return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'packing'}, (tmp) => { - const tmpTarget = path.join(tmp, path.basename(target)) - return tarPack(tmpTarget, dir, mani).then(() => { - return move(tmpTarget, target, {Promise: BB, fs}) - }).then(() => target) + return readJson(path.join(dir, 'package.json')).then((pkg) => { + return lifecycle(pkg, 'prepack', dir) + }).then(() => { + return readJson(path.join(dir, 'package.json')) + }).then((pkg) => { + return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'packing'}, (tmp) => { + const tmpTarget = path.join(tmp, path.basename(target)) + return tarPack(tmpTarget, dir, pkg).then(() => { + return move(tmpTarget, target, {Promise: BB, fs}) + }).then(() => { + return lifecycle(pkg, 'postpack', dir) + }).then(() => target) + }) }) } + +const PASSTHROUGH_OPTS = [ + 'always-auth', + 'auth-type', + 'ca', + 'cafile', + 'cert', + 'git', + 'local-address', + 'maxsockets', + 'offline', + 'prefer-offline', + 'prefer-online', + 'proxy', + 'https-proxy', + 'registry', + 'send-metrics', + 'sso-poll-frequency', + 'sso-type', + 'strict-ssl' +] + +module.exports.packGitDep = packGitDep +function packGitDep (manifest, dir) { + const stream = new PassThrough() + readJson(path.join(dir, 'package.json')).then((pkg) => { + if (pkg.scripts && pkg.scripts.prepare) { + log.verbose('prepareGitDep', `${manifest._spec}: installing devDeps and running prepare script.`) + const cliArgs = PASSTHROUGH_OPTS.reduce((acc, opt) => { + if (npm.config.get(opt, 'cli') != null) { + acc.push(`--${opt}=${npm.config.get(opt)}`) + } + return acc + }, []) + const child = cp.spawn(process.env.NODE || process.execPath, [ + require.main.filename, + 'install', + '--ignore-prepublish', + '--no-progress', + '--no-save' + ].concat(cliArgs), { + cwd: dir, + env: process.env + }) + let errData = [] + let errDataLen = 0 + let outData = [] + let outDataLen = 0 + child.stdout.on('data', (data) => { + outData.push(data) + outDataLen += data.length + log.gauge.pulse('preparing git package') + }) + child.stderr.on('data', (data) => { + errData.push(data) + errDataLen += data.length + log.gauge.pulse('preparing git package') + }) + return BB.fromNode((cb) => { + child.on('error', cb) + child.on('exit', (code, signal) => { + if (code > 0) { + const err = new Error(`${signal}: npm exited with code ${code} while attempting to build ${manifest._requested}. Clone the repository manually and run 'npm install' in it for more information.`) + err.code = code + err.signal = signal + cb(err) + } else { + cb() + } + }) + }).then(() => { + if (outDataLen > 0) log.silly('prepareGitDep', '1>', Buffer.concat(outData, outDataLen).toString()) + if (errDataLen > 0) log.silly('prepareGitDep', '2>', Buffer.concat(errData, errDataLen).toString()) + }, (err) => { + if (outDataLen > 0) log.error('prepareGitDep', '1>', Buffer.concat(outData, outDataLen).toString()) + if (errDataLen > 0) log.error('prepareGitDep', '2>', Buffer.concat(errData, errDataLen).toString()) + throw err + }) + } + }).then(() => { + return readJson(path.join(dir, 'package.json')) + }).then((pkg) => { + return cacache.tmp.withTmp(npm.tmp, { + tmpPrefix: 'pacote-packing' + }, (tmp) => { + const tmpTar = path.join(tmp, 'package.tgz') + return packDirectory(manifest, dir, tmpTar).then(() => { + return pipe(fs.createReadStream(tmpTar), stream) + }) + }) + }).catch((err) => stream.emit('error', err)) + return stream +} diff --git a/deps/npm/lib/prune.js b/deps/npm/lib/prune.js index 39d1c8ffb7..6027745383 100644 --- a/deps/npm/lib/prune.js +++ b/deps/npm/lib/prune.js @@ -26,6 +26,7 @@ function prune (args, cb) { function Pruner (where, dryrun, args) { Installer.call(this, where, dryrun, args) + this.fakeChildren = false } util.inherits(Pruner, Installer) @@ -59,7 +60,7 @@ Pruner.prototype.loadAllDepsIntoIdealTree = function (cb) { var toPrune = this.idealTree.children.filter(shouldPrune).map(getModuleName).filter(matchesArg).map(nameObj) steps.push( - [removeDeps, toPrune, this.idealTree, null, cg.newGroup('removeDeps')], + [removeDeps, toPrune, this.idealTree, null], [loadExtraneous, this.idealTree, cg.newGroup('loadExtraneous')]) chain(steps, cb) } diff --git a/deps/npm/lib/publish.js b/deps/npm/lib/publish.js index 49c98fb8e6..5d99bfd089 100644 --- a/deps/npm/lib/publish.js +++ b/deps/npm/lib/publish.js @@ -76,15 +76,23 @@ function publish_ (arg) { } function publishFromDirectory (arg) { - return pack.prepareDirectory(arg).tap((pkg) => { + // All this readJson is because any of the given scripts might modify the + // package.json in question, so we need to refresh after every step. + return pack.prepareDirectory(arg).then(() => { + return readJson(path.join(arg, 'package.json')) + }).then((pkg) => { return lifecycle(pkg, 'prepublishOnly', arg) - }).tap((pkg) => { + }).then(() => { + return readJson(path.join(arg, 'package.json')) + }).then((pkg) => { return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'fromDir'}, (tmpDir) => { const target = path.join(tmpDir, 'package.tgz') return pack.packDirectory(pkg, arg, target).then(() => { return upload(arg, pkg, false, target) }) }) + }).then(() => { + return readJson(path.join(arg, 'package.json')) }).tap((pkg) => { return lifecycle(pkg, 'publish', arg) }).tap((pkg) => { diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js index 75fe0dd95d..428c12bba7 100644 --- a/deps/npm/lib/shrinkwrap.js +++ b/deps/npm/lib/shrinkwrap.js @@ -9,7 +9,6 @@ const getRequested = require('./install/get-requested.js') const id = require('./install/deps.js') const iferr = require('iferr') const isDevDep = require('./install/is-dev-dep.js') -const isExtraneous = require('./install/is-extraneous.js') const isOptDep = require('./install/is-opt-dep.js') const isProdDep = require('./install/is-prod-dep.js') const lifecycle = require('./utils/lifecycle.js') @@ -17,9 +16,7 @@ const log = require('npmlog') const moduleName = require('./utils/module-name.js') const move = require('move-concurrently') const npm = require('./npm.js') -const packageId = require('./utils/package-id.js') const path = require('path') -const pkgSri = require('./utils/package-integrity.js') const readPackageTree = BB.promisify(require('read-package-tree')) const ssri = require('ssri') const validate = require('aproba') @@ -92,33 +89,21 @@ function treeToShrinkwrap (tree) { var pkginfo = {} if (tree.package.name) pkginfo.name = tree.package.name if (tree.package.version) pkginfo.version = tree.package.version - var problems = [] if (tree.children.length) { - shrinkwrapDeps(problems, pkginfo.dependencies = {}, tree, tree) + shrinkwrapDeps(pkginfo.dependencies = {}, tree, tree) } - if (problems.length) pkginfo.problems = problems return pkginfo } -function shrinkwrapDeps (problems, deps, top, tree, seen) { - validate('AOOO', [problems, deps, top, tree]) +function shrinkwrapDeps (deps, top, tree, seen) { + validate('OOO', [deps, top, tree]) if (!seen) seen = {} if (seen[tree.path]) return seen[tree.path] = true - Object.keys(tree.missingDeps).forEach(function (name) { - var invalid = tree.children.filter(function (dep) { return moduleName(dep) === name })[0] - if (invalid) { - problems.push('invalid: have ' + invalid.package._id + ' (expected: ' + tree.missingDeps[name] + ') ' + invalid.path) - } else if (!tree.package.optionalDependencies || !tree.package.optionalDependencies[name]) { - var topname = packageId(tree) - problems.push('missing: ' + name + '@' + tree.package.dependencies[name] + - (topname ? ', required by ' + topname : '')) - } - }) tree.children.sort(function (aa, bb) { return moduleName(aa).localeCompare(moduleName(bb)) }).forEach(function (child) { var childIsOnlyDev = isOnlyDev(child) - if (child.package._injectedFromShrinkwrap) { - deps[moduleName(child)] = child.package._injectedFromShrinkwrap + if (child.fakeChild) { + deps[moduleName(child)] = child.fakeChild return } var pkginfo = deps[moduleName(child)] = {} @@ -148,16 +133,9 @@ function shrinkwrapDeps (problems, deps, top, tree, seen) { } if (childIsOnlyDev) pkginfo.dev = true if (isOptional(child)) pkginfo.optional = true - if (isExtraneous(child)) { - problems.push('extraneous: ' + child.package._id + ' ' + child.path) - } - id.validatePeerDeps(child, function (tree, pkgname, version) { - problems.push('peer invalid: ' + pkgname + '@' + version + - ', required by ' + child.package._id) - }) if (child.children.length) { pkginfo.dependencies = {} - shrinkwrapDeps(problems, pkginfo.dependencies, top, child, seen) + shrinkwrapDeps(pkginfo.dependencies, top, child, seen) } }) } @@ -205,7 +183,6 @@ function updateLockfileMetadata (pkginfo, pkgJson) { let metainfoWritten = false const metainfo = new Set([ 'lockfileVersion', - 'packageIntegrity', 'preserveSymlinks' ]) Object.keys(pkginfo).forEach((k) => { @@ -224,7 +201,6 @@ function updateLockfileMetadata (pkginfo, pkgJson) { } function writeMetainfo (pkginfo) { pkginfo.lockfileVersion = PKGLOCK_VERSION - pkginfo.packageIntegrity = pkgJson && pkgSri.hash(pkgJson) if (process.env.NODE_PRESERVE_SYMLINKS) { pkginfo.preserveSymlinks = process.env.NODE_PRESERVE_SYMLINKS } diff --git a/deps/npm/lib/uninstall.js b/deps/npm/lib/uninstall.js index 9e3d91ac40..c181fdc4e8 100644 --- a/deps/npm/lib/uninstall.js +++ b/deps/npm/lib/uninstall.js @@ -2,24 +2,21 @@ // remove a package. module.exports = uninstall -module.exports.Uninstaller = Uninstaller -var util = require('util') -var path = require('path') -var validate = require('aproba') -var chain = require('slide').chain -var readJson = require('read-package-json') -var npm = require('./npm.js') -var Installer = require('./install.js').Installer -var getSaveType = require('./install/save.js').getSaveType -var removeDeps = require('./install/deps.js').removeDeps -var loadExtraneous = require('./install/deps.js').loadExtraneous -var log = require('npmlog') -var usage = require('./utils/usage') +const path = require('path') +const validate = require('aproba') +const readJson = require('read-package-json') +const iferr = require('iferr') +const npm = require('./npm.js') +const Installer = require('./install.js').Installer +const getSaveType = require('./install/save.js').getSaveType +const removeDeps = require('./install/deps.js').removeDeps +const log = require('npmlog') +const usage = require('./utils/usage') uninstall.usage = usage( 'uninstall', - 'npm uninstall [<@scope>/]<pkg>[@<version>]... [--save|--save-dev|--save-optional]' + 'npm uninstall [<@scope>/]<pkg>[@<version>]... [--save-prod|--save-dev|--save-optional] [--no-save]' ) uninstall.completion = require('./utils/completion/installed-shallow.js') @@ -27,17 +24,18 @@ uninstall.completion = require('./utils/completion/installed-shallow.js') function uninstall (args, cb) { validate('AF', arguments) // the /path/to/node_modules/.. - var dryrun = !!npm.config.get('dry-run') + const dryrun = !!npm.config.get('dry-run') if (args.length === 1 && args[0] === '.') args = [] - args = args.filter(function (a) { - return path.resolve(a) !== where - }) - var where = npm.config.get('global') || !args.length + const where = npm.config.get('global') || !args.length ? path.resolve(npm.globalDir, '..') : npm.prefix + args = args.filter(function (a) { + return path.resolve(a) !== where + }) + if (args.length) { new Uninstaller(where, dryrun, args).run(cb) } else { @@ -50,29 +48,33 @@ function uninstall (args, cb) { } } -function Uninstaller (where, dryrun, args) { - validate('SBA', arguments) - Installer.call(this, where, dryrun, args) -} -util.inherits(Uninstaller, Installer) +class Uninstaller extends Installer { + constructor (where, dryrun, args) { + super(where, dryrun, args) + this.remove = [] + this.fakeChildren = false + } -Uninstaller.prototype.loadArgMetadata = function (next) { - this.args = this.args.map(function (arg) { return {name: arg} }) - next() -} + loadArgMetadata (next) { + this.args = this.args.map(function (arg) { return {name: arg} }) + next() + } -Uninstaller.prototype.loadAllDepsIntoIdealTree = function (cb) { - validate('F', arguments) - log.silly('uninstall', 'loadAllDepsIntoIdealTree') - var saveDeps = getSaveType() + loadAllDepsIntoIdealTree (cb) { + validate('F', arguments) + this.remove = this.args + this.args = [] + log.silly('uninstall', 'loadAllDepsIntoIdealTree') + const saveDeps = getSaveType() - var cg = this.progress['loadIdealTree:loadAllDepsIntoIdealTree'] - var steps = [] - steps.push( - [removeDeps, this.args, this.idealTree, saveDeps, cg.newGroup('removeDeps')], - [loadExtraneous, this.idealTree, cg.newGroup('loadExtraneous')]) - chain(steps, cb) + super.loadAllDepsIntoIdealTree(iferr(cb, () => { + removeDeps(this.remove, this.idealTree, saveDeps, cb) + })) + } + + // no top level lifecycles on rm + runPreinstallTopLevelLifecycles (cb) { cb() } + runPostinstallTopLevelLifecycles (cb) { cb() } } -Uninstaller.prototype.runPreinstallTopLevelLifecycles = function (cb) { cb() } -Uninstaller.prototype.runPostinstallTopLevelLifecycles = function (cb) { cb() } +module.exports.Uninstaller = Uninstaller diff --git a/deps/npm/lib/utils/error-handler.js b/deps/npm/lib/utils/error-handler.js index 8365f39d9d..5374d1feec 100644 --- a/deps/npm/lib/utils/error-handler.js +++ b/deps/npm/lib/utils/error-handler.js @@ -130,10 +130,12 @@ function exit (code, noLog) { itWorked = !code - // just emit a fake exit event. - // if we're really exiting, then let it exit on its own, so that - // in-process stuff can finish or clean up first. - if (!doExit) process.emit('exit', code) + // Exit directly -- nothing in the CLI should still be running in the + // background at this point, and this makes sure anything left dangling + // for whatever reason gets thrown away, instead of leaving the CLI open + // + // Commands that expect long-running actions should just delay `cb()` + process.exit(code) } } diff --git a/deps/npm/lib/utils/link.js b/deps/npm/lib/utils/link.js index 605b77402c..15331740a4 100644 --- a/deps/npm/lib/utils/link.js +++ b/deps/npm/lib/utils/link.js @@ -64,7 +64,7 @@ function link (from, to, gently, abs, cb) { [ [ensureFromIsNotSource, absTarget, to], [fs, 'stat', absTarget], - [rm, to, gently], + [rm, to, gently, path.dirname(to)], [mkdir, path.dirname(to)], [fs, 'symlink', target, to, 'junction'] ], diff --git a/deps/npm/lib/utils/package-integrity.js b/deps/npm/lib/utils/package-integrity.js deleted file mode 100644 index f9560d660e..0000000000 --- a/deps/npm/lib/utils/package-integrity.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -// Utilities for generating and verifying the packageIntegrity field for -// package-lock -// -// Spec: https://github.com/npm/npm/pull/16441 - -const ssri = require('ssri') -const SSRI_OPTS = { - algorithms: ['sha512'] -} - -module.exports.check = check -function check (pkg, integrity) { - return ssri.checkData(JSON.stringify(pkg), integrity, SSRI_OPTS) -} - -module.exports.hash = hash -function hash (pkg) { - return ssri.fromData(JSON.stringify(pkg), SSRI_OPTS).toString() -} diff --git a/deps/npm/lib/utils/tar.js b/deps/npm/lib/utils/tar.js index 7ebc9d6875..ebbee025a2 100644 --- a/deps/npm/lib/utils/tar.js +++ b/deps/npm/lib/utils/tar.js @@ -3,8 +3,6 @@ // commands for packing and unpacking tarballs // this file is used by lib/cache.js -const BB = require('bluebird') - var fs = require('graceful-fs') var path = require('path') var writeFileAtomic = require('write-file-atomic') @@ -28,11 +26,6 @@ var moduleName = require('./module-name.js') var packageId = require('./package-id.js') var pulseTillDone = require('../utils/pulse-till-done.js') -const cacache = require('cacache') -const packAsync = BB.promisify(pack) -const PassThrough = require('stream').PassThrough -const pipe = BB.promisify(require('mississippi').pipe) - if (process.env.SUDO_UID && myUid === 0) { if (!isNaN(process.env.SUDO_UID)) myUid = +process.env.SUDO_UID if (!isNaN(process.env.SUDO_GID)) myGid = +process.env.SUDO_GID @@ -41,18 +34,6 @@ if (process.env.SUDO_UID && myUid === 0) { exports.pack = pack exports.unpack = unpack -module.exports.packToStream = packToStream -function packToStream (mani, dir) { - const stream = new PassThrough() - cacache.tmp.withTmp(npm.tmp, (tmp) => { - const tmpTarget = path.join(tmp, 'package.tgz') - return packAsync(tmpTarget, dir, mani).then(() => { - return pipe(fs.createReadStream(tmpTarget), stream) - }) - }).catch((err) => stream.emit('error', err)) - return stream -} - function pack (tarball, folder, pkg, cb) { log.verbose('tar pack', [tarball, folder]) |