Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/dedupe.js19
-rw-r--r--lib/fetch-package-metadata.js48
-rw-r--r--lib/install.js65
-rw-r--r--lib/install/action/extract.js8
-rw-r--r--lib/install/action/finalize.js120
-rw-r--r--lib/install/action/refresh-package-json.js36
-rw-r--r--lib/install/action/update-linked.js16
-rw-r--r--lib/install/actions.js8
-rw-r--r--lib/install/copy-tree.js12
-rw-r--r--lib/install/decompose-actions.js5
-rw-r--r--lib/install/deps.js164
-rw-r--r--lib/install/diff-trees.js20
-rw-r--r--lib/install/filter-invalid-actions.js36
-rw-r--r--lib/install/get-requested.js12
-rw-r--r--lib/install/inflate-bundled.js4
-rw-r--r--lib/install/inflate-shrinkwrap.js77
-rw-r--r--lib/install/node.js21
-rw-r--r--lib/install/read-shrinkwrap.js2
-rw-r--r--lib/install/realize-shrinkwrap-specifier.js16
-rw-r--r--lib/install/save.js55
-rw-r--r--lib/install/update-package-json.js2
-rw-r--r--lib/ls.js9
-rw-r--r--lib/outdated.js9
-rw-r--r--lib/prune.js4
-rw-r--r--lib/shrinkwrap.js71
-rw-r--r--lib/uninstall.js3
-rw-r--r--lib/utils/tar.js9
27 files changed, 451 insertions, 400 deletions
diff --git a/lib/dedupe.js b/lib/dedupe.js
index 9e6dd1ef2..ded309c21 100644
--- a/lib/dedupe.js
+++ b/lib/dedupe.js
@@ -13,13 +13,13 @@ var earliestInstallable = require('./install/deps.js').earliestInstallable
var checkPermissions = require('./install/check-permissions.js')
var decomposeActions = require('./install/decompose-actions.js')
var loadExtraneous = require('./install/deps.js').loadExtraneous
-var filterInvalidActions = require('./install/filter-invalid-actions.js')
-var recalculateMetadata = require('./install/deps.js').recalculateMetadata
+var computeMetadata = require('./install/deps.js').computeMetadata
var sortActions = require('./install/diff-trees.js').sortActions
var moduleName = require('./utils/module-name.js')
var packageId = require('./utils/package-id.js')
var childPath = require('./utils/child-path.js')
var usage = require('./utils/usage')
+var getRequested = require('./install/get-requested.js')
module.exports = dedupe
module.exports.Deduper = Deduper
@@ -65,10 +65,16 @@ Deduper.prototype.loadIdealTree = function (cb) {
} ],
[this, this.finishTracker, 'loadAllDepsIntoIdealTree'],
- [this, function (next) { recalculateMetadata(this.idealTree, log, next) }]
+ [this, andComputeMetadata(this.idealTree)]
], cb)
}
+function andComputeMetadata (tree) {
+ return function (next) {
+ next(null, computeMetadata(tree))
+ }
+}
+
Deduper.prototype.generateActionsToTake = function (cb) {
validate('F', arguments)
log.silly('dedupe', 'generateActionsToTake')
@@ -82,7 +88,6 @@ Deduper.prototype.generateActionsToTake = function (cb) {
next()
}],
[this, this.finishTracker, 'sort-actions'],
- [filterInvalidActions, this.where, this.differences],
[checkPermissions, this.differences],
[decomposeActions, this.differences, this.todo]
], cb)
@@ -130,18 +135,18 @@ function hoistChildren_ (tree, diff, seen, next) {
seen[tree.path] = true
asyncMap(tree.children, function (child, done) {
if (!tree.parent) return hoistChildren_(child, diff, seen, done)
- var better = findRequirement(tree.parent, moduleName(child), child.package._requested || npa(packageId(child)))
+ var better = findRequirement(tree.parent, moduleName(child), getRequested(child) || npa(packageId(child)))
if (better) {
return chain([
[remove, child, diff],
- [recalculateMetadata, tree, log]
+ [andComputeMetadata(tree)]
], done)
}
var hoistTo = earliestInstallable(tree, tree.parent, child.package)
if (hoistTo) {
move(child, hoistTo, diff)
chain([
- [recalculateMetadata, hoistTo, log],
+ [andComputeMetadata(hoistTo)],
[hoistChildren_, child, diff, seen],
[ function (next) {
moveRemainingChildren(child, diff)
diff --git a/lib/fetch-package-metadata.js b/lib/fetch-package-metadata.js
index 2951cb47a..fcd548de8 100644
--- a/lib/fetch-package-metadata.js
+++ b/lib/fetch-package-metadata.js
@@ -1,6 +1,7 @@
'use strict'
const deprCheck = require('./utils/depr-check')
+const path = require('path')
const log = require('npmlog')
const readPackageTree = require('read-package-tree')
const rimraf = require('rimraf')
@@ -12,12 +13,13 @@ const limit = require('call-limit')
const tempFilename = require('./utils/temp-filename')
const pacote = require('pacote')
const pacoteOpts = require('./config/pacote')
+const isWindows = require('./utils/is-windows.js')
function andLogAndFinish (spec, tracker, done) {
- validate('SF', [spec, done])
+ validate('SOF|SZF|OOF|OZF', [spec, tracker, done])
return (er, pkg) => {
if (er) {
- log.silly('fetchPackageMetaData', 'error for ' + spec, er)
+ log.silly('fetchPackageMetaData', 'error for ' + String(spec), er)
if (tracker) tracker.finish()
}
return done(er, pkg)
@@ -38,12 +40,20 @@ function fetchPackageMetadata (spec, where, opts, done) {
opts = {}
}
var tracker = opts.tracker
+ const logAndFinish = andLogAndFinish(spec, tracker, done)
+
if (typeof spec === 'object') {
var dep = spec
- spec = dep.raw
+ } else {
+ dep = npa(spec)
}
- const logAndFinish = andLogAndFinish(spec, tracker, done)
- pacote.manifest(dep || spec, pacoteOpts({
+ if (!isWindows && dep.type === 'directory' && /^[a-zA-Z]:/.test(dep.fetchSpec)) {
+ var err = new Error(`Can't install from windows path on a non-windows system: ${dep.fetchSpec.replace(/[/]/g, '\\')}`)
+ err.code = 'EWINDOWSPATH'
+ return logAndFinish(err)
+ }
+
+ pacote.manifest(dep, pacoteOpts({
annotate: true,
fullMetadata: opts.fullMetadata,
log: tracker || npmlog,
@@ -51,7 +61,22 @@ function fetchPackageMetadata (spec, where, opts, done) {
where: where
})).then(
(pkg) => logAndFinish(null, deprCheck(pkg)),
- logAndFinish
+ (err) => {
+ if (dep.type !== 'directory') return logAndFinish(err)
+ if (err.code === 'ENOTDIR') {
+ var enolocal = new Error(`Could not install "${path.relative(process.cwd(), dep.fetchSpec)}" as it is not a directory and is not a file with a name ending in .tgz, .tar.gz or .tar`)
+ enolocal.code = 'ENOLOCAL'
+ if (err.stack) enolocal.stack = err.stack
+ return logAndFinish(enolocal)
+ } else if (err.code === 'ENOPACKAGEJSON') {
+ var enopackage = new Error(`Could not install from "${path.relative(process.cwd(), dep.fetchSpec)}" as it does not contain a package.json file.`)
+ enopackage.code = 'ENOLOCAL'
+ if (err.stack) enopackage.stack = err.stack
+ return logAndFinish(enopackage)
+ } else {
+ return logAndFinish(err)
+ }
+ }
)
}
@@ -59,7 +84,16 @@ module.exports.addBundled = addBundled
function addBundled (pkg, next) {
validate('OF', arguments)
if (pkg._bundled !== undefined) return next(null, pkg)
- if (!pkg.bundleDependencies) return next(null, pkg)
+
+ if (!pkg.bundleDependencies && pkg._requested.type !== 'directory') return next(null, pkg)
+ const requested = pkg._requested || npa(pkg._from)
+ if (requested.type === 'directory') {
+ pkg._bundled = null
+ return readPackageTree(pkg._requested.fetchSpec, function (er, tree) {
+ if (tree) pkg._bundled = tree.children
+ return next(null, pkg)
+ })
+ }
pkg._bundled = null
const target = tempFilename('unpack')
const opts = pacoteOpts({integrity: pkg._integrity})
diff --git a/lib/install.js b/lib/install.js
index fbd82a41b..fc6ca3463 100644
--- a/lib/install.js
+++ b/lib/install.js
@@ -118,7 +118,7 @@ var saveMetrics = require('./utils/metrics.js').save
// install specific libraries
var copyTree = require('./install/copy-tree.js')
var readShrinkwrap = require('./install/read-shrinkwrap.js')
-var recalculateMetadata = require('./install/deps.js').recalculateMetadata
+var computeMetadata = require('./install/deps.js').computeMetadata
var prefetchDeps = require('./install/deps.js').prefetchDeps
var loadDeps = require('./install/deps.js').loadDeps
var loadDevDeps = require('./install/deps.js').loadDevDeps
@@ -128,7 +128,6 @@ var loadExtraneous = require('./install/deps.js').loadExtraneous
var diffTrees = require('./install/diff-trees.js')
var checkPermissions = require('./install/check-permissions.js')
var decomposeActions = require('./install/decompose-actions.js')
-var filterInvalidActions = require('./install/filter-invalid-actions.js')
var validateTree = require('./install/validate-tree.js')
var validateArgs = require('./install/validate-args.js')
var saveRequested = require('./install/save.js').saveRequested
@@ -141,6 +140,8 @@ var removeObsoleteDep = require('./install/deps.js').removeObsoleteDep
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) {
validate('SSF', arguments)
@@ -281,8 +282,12 @@ Installer.prototype.run = function (_cb) {
[this, this.runPostinstallTopLevelLifecycles],
[this, this.finishTracker, 'runTopLevelLifecycles']
)
- if (getSaveType(this.args)) {
- postInstallSteps.push([this, this.saveToDependencies])
+ if (getSaveType()) {
+ postInstallSteps.push(
+ // this is necessary as we don't fill in `dependencies` and `devDependencies` in deps loaded from shrinkwrap
+ // until after we extract them
+ [this, (next) => { computeMetadata(this.idealTree); next() }],
+ [this, this.saveToDependencies])
}
}
postInstallSteps.push(
@@ -340,11 +345,24 @@ Installer.prototype.loadCurrentTree = function (cb) {
} else {
todo.push([this, this.readLocalPackageData])
}
- todo.push(
- [this, this.normalizeTree, log.newGroup('loadCurrentTree:normalizeTree')])
+ todo.push([this, this.normalizeCurrentTree])
chain(todo, cb)
}
+var createNode = require('./install/node.js').create
+var flatNameFromTree = require('./install/flatten-tree.js').flatNameFromTree
+Installer.prototype.normalizeCurrentTree = function (cb) {
+ this.currentTree.isTop = true
+ normalizeTree(this.currentTree)
+ return cb()
+
+ function normalizeTree (tree) {
+ createNode(tree)
+ tree.location = flatNameFromTree(tree)
+ tree.children.forEach(normalizeTree)
+ }
+}
+
Installer.prototype.loadIdealTree = function (cb) {
validate('F', arguments)
log.silly('install', 'loadIdealTree')
@@ -361,16 +379,22 @@ Installer.prototype.loadIdealTree = function (cb) {
[this.newTracker(this.progress.loadIdealTree, 'loadIdealTree:loadAllDepsIntoIdealTree', 10)],
[this, this.loadAllDepsIntoIdealTree],
[this, this.finishTracker, 'loadIdealTree:loadAllDepsIntoIdealTree'],
-
- // TODO: Remove this (should no longer be necessary, instead counter productive)
- [this, function (next) { recalculateMetadata(this.idealTree, log, next) }]
+ [this, function (next) { computeMetadata(this.idealTree); next() }],
+ [this, this.pruneIdealTree]
], cb)
}
+Installer.prototype.pruneIdealTree = function (cb) {
+ var toPrune = this.idealTree.children
+ .filter((n) => !n.fromShrinkwrap && isExtraneous(n))
+ .map((n) => ({name: moduleName(n)}))
+ return removeDeps(toPrune, this.idealTree, null, log.newGroup('pruneDeps'), cb)
+}
+
Installer.prototype.loadAllDepsIntoIdealTree = function (cb) {
validate('F', arguments)
log.silly('install', 'loadAllDepsIntoIdealTree')
- var saveDeps = getSaveType(this.args)
+ var saveDeps = getSaveType()
var cg = this.progress['loadIdealTree:loadAllDepsIntoIdealTree']
var installNewModules = !!this.args.length
@@ -386,8 +410,7 @@ Installer.prototype.loadAllDepsIntoIdealTree = function (cb) {
)
if (this.prod || this.dev) {
steps.push(
- [prefetchDeps, this.idealTree, depsToPreload, cg.newGroup('prefetchDeps')]
- )
+ [prefetchDeps, this.idealTree, depsToPreload, cg.newGroup('prefetchDeps')])
}
if (this.prod) {
steps.push(
@@ -411,7 +434,6 @@ Installer.prototype.generateActionsToTake = function (cb) {
[validateTree, this.idealTree, cg.newGroup('validateTree')],
[diffTrees, this.currentTree, this.idealTree, this.differences, cg.newGroup('diffTrees')],
[this, this.computeLinked],
- [filterInvalidActions, this.where, this.differences],
[checkPermissions, this.differences],
[decomposeActions, this.differences, this.todo]
], cb)
@@ -474,10 +496,11 @@ Installer.prototype.executeActions = function (cb) {
[lock, node_modules, '.staging'],
[rimraf, staging],
[doParallelActions, 'extract', staging, todo, cg.newGroup('extract', 100)],
- [doParallelActions, 'preinstall', staging, todo, trackLifecycle.newGroup('preinstall')],
[doReverseSerialActions, 'remove', staging, todo, cg.newGroup('remove')],
[doSerialActions, 'move', staging, todo, cg.newGroup('move')],
[doSerialActions, 'finalize', staging, todo, cg.newGroup('finalize')],
+ [doParallelActions, 'refresh-package-json', staging, todo, cg.newGroup('refresh-package-json')],
+ [doParallelActions, 'preinstall', staging, todo, trackLifecycle.newGroup('preinstall')],
[doSerialActions, 'build', staging, todo, trackLifecycle.newGroup('build')],
[doSerialActions, 'global-link', staging, todo, trackLifecycle.newGroup('global-link')],
[doParallelActions, 'update-linked', staging, todo, trackLifecycle.newGroup('update-linked')],
@@ -557,6 +580,7 @@ Installer.prototype.runPostinstallTopLevelLifecycles = function (cb) {
Installer.prototype.saveToDependencies = function (cb) {
validate('F', arguments)
if (this.failing) return cb()
+ if (!this.differences.length) return cb()
log.silly('install', 'saveToDependencies')
saveRequested(this.args, this.idealTree, cb)
}
@@ -623,19 +647,6 @@ Installer.prototype.loadShrinkwrap = function (cb) {
readShrinkwrap.andInflate(this.idealTree, cb)
}
-Installer.prototype.normalizeTree = function (log, cb) {
- validate('OF', arguments)
- log.silly('install', 'normalizeTree')
- recalculateMetadata(this.currentTree, log, iferr(cb, function (tree) {
- tree.children.forEach(function (child) {
- if (child.requiredBy.length === 0) {
- child.existing = true
- }
- })
- cb(null, tree)
- }))
-}
-
Installer.prototype.getInstalledModules = function () {
return this.differences.filter(function (action) {
var mutation = action[0]
diff --git a/lib/install/action/extract.js b/lib/install/action/extract.js
index ea2d50935..4693f4940 100644
--- a/lib/install/action/extract.js
+++ b/lib/install/action/extract.js
@@ -15,10 +15,9 @@ const pacote = require('pacote')
const pacoteOpts = require('../../config/pacote')
const path = require('path')
const readJson = BB.promisify(require('read-package-json'))
-const updatePackageJson = BB.promisify(require('../update-package-json'))
module.exports = extract
-function extract (staging, pkg, log, next) {
+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')
@@ -49,14 +48,13 @@ function extract (staging, pkg, log, next) {
delete metadata.readmeFilename
pkg.package = metadata
}
- return updatePackageJson(pkg, extractTo)
}).then(() => {
if (pkg.package.bundleDependencies) {
return readBundled(pkg, staging, extractTo)
}
}).then(() => {
return gentlyRm(path.join(extractTo, 'node_modules'))
- }).then(() => next(), next)
+ })
}
function readBundled (pkg, staging, extractTo) {
@@ -101,8 +99,6 @@ function finishModule (bundler, child, stageTo, stageFrom) {
if (child.fromBundle === bundler) {
return mkdirp(path.dirname(stageTo)).then(() => {
return move(stageFrom, stageTo)
- }).then(() => {
- return updatePackageJson(child, stageTo)
})
} else {
return fs.statAsync(stageFrom).then(() => {
diff --git a/lib/install/action/finalize.js b/lib/install/action/finalize.js
index 03a71f4cc..283e7f14e 100644
--- a/lib/install/action/finalize.js
+++ b/lib/install/action/finalize.js
@@ -1,85 +1,83 @@
'use strict'
-var path = require('path')
-var rimraf = require('rimraf')
-var fs = require('graceful-fs')
-var mkdirp = require('mkdirp')
-var asyncMap = require('slide').asyncMap
-var move = require('../../utils/move.js')
-var gentlyRm = require('../../utils/gently-rm')
-var moduleStagingPath = require('../module-staging-path.js')
+const path = require('path')
+const fs = require('graceful-fs')
+const Bluebird = require('bluebird')
+const rimraf = Bluebird.promisify(require('rimraf'))
+const mkdirp = Bluebird.promisify(require('mkdirp'))
+const lstat = Bluebird.promisify(fs.lstat)
+const readdir = Bluebird.promisify(fs.readdir)
+const symlink = Bluebird.promisify(fs.symlink)
+const gentlyRm = require('../../utils/gently-rm')
+const moduleStagingPath = require('../module-staging-path.js')
+const move = require('move-concurrently')
+const moveOpts = {fs: fs, Promise: Bluebird, maxConcurrency: 4}
+const getRequested = require('../get-requested.js')
-module.exports = function (staging, pkg, log, next) {
- log.silly('finalize', pkg.path)
+module.exports = function (staging, pkg, log) {
+ log.silly('finalize', pkg.realpath)
- var extractedTo = moduleStagingPath(staging, pkg)
+ const extractedTo = moduleStagingPath(staging, pkg)
- var delpath = path.join(path.dirname(pkg.path), '.' + path.basename(pkg.path) + '.DELETE')
+ const delpath = path.join(path.dirname(pkg.realpath), '.' + path.basename(pkg.realpath) + '.DELETE')
+ let movedDestAway = false
- mkdirp(path.resolve(pkg.path, '..'), whenParentExists)
-
- function whenParentExists (mkdirEr) {
- if (mkdirEr) return next(mkdirEr)
- // We stat first, because we can't rely on ENOTEMPTY from Windows.
- // Windows, by contrast, gives the generic EPERM of a folder already exists.
- fs.lstat(pkg.path, destStatted)
- }
-
- function destStatted (doesNotExist) {
- if (doesNotExist) {
- move(extractedTo, pkg.path, whenMoved)
- } else {
- moveAway()
- }
- }
-
- function whenMoved (moveEr) {
- if (!moveEr) return next()
- if (moveEr.code !== 'ENOTEMPTY' && moveEr.code !== 'EEXIST') return next(moveEr)
- moveAway()
+ const requested = pkg.package._requested || getRequested(pkg)
+ if (requested.type === 'directory') {
+ return makeParentPath(pkg.path)
+ .then(() => symlink(pkg.realpath, pkg.path, 'junction'))
+ } else {
+ return makeParentPath(pkg.realpath)
+ .then(moveStagingToDestination)
+ .then(restoreOldNodeModules)
+ .catch((err) => {
+ if (movedDestAway) {
+ return rimraf(pkg.realpath).then(moveOldDestinationBack).thenReturn(Promise.reject(err))
+ } else {
+ return Promise.reject(err)
+ }
+ })
+ .then(() => rimraf(delpath))
}
- function moveAway () {
- move(pkg.path, delpath, whenOldMovedAway)
+ function makeParentPath (dir) {
+ return mkdirp(path.dirname(dir))
}
- function whenOldMovedAway (moveEr) {
- if (moveEr) return next(moveEr)
- move(extractedTo, pkg.path, whenConflictMoved)
+ function moveStagingToDestination () {
+ return destinationIsClear()
+ .then(actuallyMoveStaging)
+ .catch(() => moveOldDestinationAway().then(actuallyMoveStaging))
}
- function whenConflictMoved (moveEr) {
- // if we got an error we'll try to put back the original module back,
- // succeed or fail though we want the original error that caused this
- if (moveEr) return move(delpath, pkg.path, function () { next(moveEr) })
- fs.readdir(path.join(delpath, 'node_modules'), makeTarget)
+ function destinationIsClear () {
+ return lstat(pkg.realpath).then(() => Bluebird.reject(new Error('destination exists')), () => Bluebird.resolve())
}
- function makeTarget (readdirEr, files) {
- if (readdirEr) return cleanup()
- if (!files.length) return cleanup()
- mkdirp(path.join(pkg.path, 'node_modules'), function (mkdirEr) { moveModules(mkdirEr, files) })
+ function actuallyMoveStaging () {
+ return move(extractedTo, pkg.realpath, moveOpts)
}
- function moveModules (mkdirEr, files) {
- if (mkdirEr) return next(mkdirEr)
- asyncMap(files, function (file, done) {
- var from = path.join(delpath, 'node_modules', file)
- var to = path.join(pkg.path, 'node_modules', file)
- move(from, to, done)
- }, cleanup)
+ function moveOldDestinationAway () {
+ return rimraf(delpath).then(() => move(pkg.realpath, delpath, moveOpts)).then(() => { movedDestAway = true })
}
- function cleanup (moveEr) {
- if (moveEr) return next(moveEr)
- rimraf(delpath, afterCleanup)
+ function moveOldDestinationBack () {
+ return move(delpath, pkg.realpath, moveOpts).then(() => { movedDestAway = false })
}
- function afterCleanup (rimrafEr) {
- if (rimrafEr) log.warn('finalize', rimrafEr)
- next()
+ function restoreOldNodeModules () {
+ if (!movedDestAway) return
+ return readdir(path.join(delpath, 'node_modules')).catch(() => []).then((modules) => {
+ if (!modules.length) return
+ return mkdirp(path.join(pkg.realpath, 'node_modules')).then(() => Bluebird.map(modules, (file) => {
+ const from = path.join(delpath, 'node_modules', file)
+ const to = path.join(pkg.realpath, 'node_modules', file)
+ return move(from, to, moveOpts)
+ }))
+ })
}
}
module.exports.rollback = function (top, staging, pkg, next) {
- gentlyRm(pkg.path, false, top, next)
+ gentlyRm(pkg.realpath, false, top, next)
}
diff --git a/lib/install/action/refresh-package-json.js b/lib/install/action/refresh-package-json.js
new file mode 100644
index 000000000..e2ad747e8
--- /dev/null
+++ b/lib/install/action/refresh-package-json.js
@@ -0,0 +1,36 @@
+'use strict'
+const path = require('path')
+const Bluebird = require('bluebird')
+const readJson = Bluebird.promisify(require('read-package-json'))
+const updatePackageJson = Bluebird.promisify(require('../update-package-json'))
+const getRequested = require('../get-requested.js')
+
+module.exports = function (staging, pkg, log) {
+ log.silly('refresh-package-json', pkg.realpath)
+
+ return readJson(path.join(pkg.path, 'package.json'), false).then((metadata) => {
+ Object.keys(pkg.package).forEach(function (key) {
+ if (!isEmpty(pkg.package[key])) {
+ metadata[key] = pkg.package[key]
+ }
+ })
+ // These two sneak in and it's awful
+ delete metadata.readme
+ delete metadata.readmeFilename
+
+ pkg.package = metadata
+ }).catch(() => 'ignore').then(() => {
+ const requested = pkg.package._requested || getRequested(pkg)
+ if (requested.type !== 'directory') {
+ return updatePackageJson(pkg, pkg.path)
+ }
+ })
+}
+
+function isEmpty (value) {
+ if (value == null) return true
+ if (Array.isArray(value)) return !value.length
+ if (typeof value === 'object') return !Object.keys(value).length
+ return false
+}
+
diff --git a/lib/install/action/update-linked.js b/lib/install/action/update-linked.js
deleted file mode 100644
index 0babe10fd..000000000
--- a/lib/install/action/update-linked.js
+++ /dev/null
@@ -1,16 +0,0 @@
-'use strict'
-var path = require('path')
-
-function getTop (pkg) {
- if (pkg.target && pkg.target.parent) return getTop(pkg.target.parent)
- if (pkg.parent) return getTop(pkg.parent)
- return pkg.path
-}
-
-module.exports = function (staging, pkg, log, next) {
- if (pkg.package.version !== pkg.oldPkg.package.version) {
- log.warn('update-linked', path.relative(getTop(pkg), 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/lib/install/actions.js b/lib/install/actions.js
index acd542bbd..23f0c93cb 100644
--- a/lib/install/actions.js
+++ b/lib/install/actions.js
@@ -24,9 +24,9 @@ actions.prepare = require('./action/prepare.js')
actions.finalize = require('./action/finalize.js')
actions.remove = require('./action/remove.js')
actions.move = require('./action/move.js')
-actions['update-linked'] = require('./action/update-linked.js')
actions['global-install'] = require('./action/global-install.js')
actions['global-link'] = require('./action/global-link.js')
+actions['refresh-package-json'] = require('./action/refresh-package-json.js')
// FIXME: We wrap actions like three ways to sunday here.
// Rewrite this to only work one way.
@@ -58,7 +58,9 @@ Object.keys(actions).forEach(function (actionName) {
}
}
function thenRunAction () {
- action(staging, pkg, log, andDone(next))
+ var done = andDone(next)
+ var result = action(staging, pkg, log, done)
+ if (result && result.then) result.then(() => done(), done)
}
function andDone (cb) {
return andFinishTracker(log, andAddParentToErrors(pkg.parent, andHandleOptionalDepErrors(pkg, cb)))
@@ -70,7 +72,7 @@ function markAsFailed (pkg) {
pkg.failed = true
pkg.requires.forEach(function (req) {
req.requiredBy = req.requiredBy.filter(function (reqReqBy) { return reqReqBy !== pkg })
- if (req.requiredBy.length === 0 && !req.userRequired && !req.existing) {
+ if (req.requiredBy.length === 0 && !req.userRequired) {
markAsFailed(req)
}
})
diff --git a/lib/install/copy-tree.js b/lib/install/copy-tree.js
index 67a9c687a..394d3ff80 100644
--- a/lib/install/copy-tree.js
+++ b/lib/install/copy-tree.js
@@ -1,12 +1,12 @@
'use strict'
-
+var createNode = require('./node.js').create
module.exports = function (tree) {
return copyTree(tree, {})
}
function copyTree (tree, cache) {
if (cache[tree.path]) return cache[tree.path]
- var newTree = cache[tree.path] = Object.create(tree)
+ var newTree = cache[tree.path] = createNode(Object.assign({}, tree))
copyModuleList(newTree, 'children', cache)
newTree.children.forEach(function (child) {
child.parent = newTree
@@ -18,8 +18,10 @@ function copyTree (tree, cache) {
function copyModuleList (tree, key, cache) {
var newList = []
- tree[key].forEach(function (child) {
- newList.push(copyTree(child, cache))
- })
+ if (tree[key]) {
+ tree[key].forEach(function (child) {
+ newList.push(copyTree(child, cache))
+ })
+ }
tree[key] = newList
}
diff --git a/lib/install/decompose-actions.js b/lib/install/decompose-actions.js
index 401e29c62..ed582ce4c 100644
--- a/lib/install/decompose-actions.js
+++ b/lib/install/decompose-actions.js
@@ -19,7 +19,6 @@ module.exports = function (differences, decomposed, next) {
moveSteps(decomposed, pkg, done)
break
case 'remove':
- case 'update-linked':
default:
defaultSteps(decomposed, cmd, pkg, done)
}
@@ -27,7 +26,7 @@ module.exports = function (differences, decomposed, next) {
}
function addSteps (decomposed, pkg, done) {
- if (!pkg.fromBundle) {
+ if (!pkg.fromBundle && !pkg.isLink) {
decomposed.push(['fetch', pkg])
decomposed.push(['extract', pkg])
}
@@ -38,6 +37,7 @@ function addSteps (decomposed, pkg, done) {
decomposed.push(['postinstall', pkg])
}
decomposed.push(['finalize', pkg])
+ decomposed.push(['refresh-package-json', pkg])
done()
}
@@ -51,6 +51,7 @@ function moveSteps (decomposed, pkg, done) {
decomposed.push(['build', pkg])
decomposed.push(['install', pkg])
decomposed.push(['postinstall', pkg])
+ decomposed.push(['refresh-package-json', pkg])
done()
}
diff --git a/lib/install/deps.js b/lib/install/deps.js
index 8b0f18a07..fe694f322 100644
--- a/lib/install/deps.js
+++ b/lib/install/deps.js
@@ -2,12 +2,12 @@
const BB = require('bluebird')
+var fs = require('fs')
var assert = require('assert')
var path = require('path')
var semver = require('semver')
var asyncMap = require('slide').asyncMap
var chain = require('slide').chain
-var union = require('lodash.union')
var iferr = require('iferr')
var npa = require('npm-package-arg')
var validate = require('aproba')
@@ -29,6 +29,7 @@ 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')
+var getSaveType = require('./save.js').getSaveType
// The export functions in this module mutate a dependency tree, adding
// items to them.
@@ -43,6 +44,11 @@ function doesChildVersionMatch (child, requested, requestor) {
// prereleases to match * if there are ONLY prereleases
if (requested.type === 'range' && requested.fetchSpec === '*') return true
+ if (requested.type === 'directory') {
+ if (!child.isLink) return false
+ return path.relative(child.realpath, requested.fetchSpec) === ''
+ }
+
if (!registryTypes[requested.type]) {
var childReq = child.package._requested
if (!childReq && child.package._from) {
@@ -70,85 +76,51 @@ function doesChildVersionMatch (child, requested, requestor) {
return semver.satisfies(child.package.version, requested.fetchSpec)
}
-// TODO: Rename to maybe computeMetadata or computeRelationships
-exports.recalculateMetadata = function (tree, log, next) {
- recalculateMetadata(tree, log, {}, next)
-}
-
function childDependencySpecifier (tree, name, spec) {
return npa.resolve(name, spec, packageRelativePath(tree))
}
-function recalculateMetadata (tree, log, seen, next) {
- validate('OOOF', arguments)
- if (seen[tree.path]) return next()
+exports.computeMetadata = computeMetadata
+function computeMetadata (tree, seen) {
+ if (!seen) seen = {}
+ if (!tree || seen[tree.path]) return
seen[tree.path] = true
if (tree.parent == null) {
resetMetadata(tree)
tree.isTop = true
}
+ tree.location = flatNameFromTree(tree)
- function markDeps (toMark, done) {
- var name = toMark.name
- var spec = toMark.spec
- var kind = toMark.kind
+ function findChild (name, spec, kind) {
try {
var req = childDependencySpecifier(tree, name, spec)
} catch (err) {
- return done()
+ return
}
- if (!req.name) return done()
var child = findRequirement(tree, req.name, req)
if (child) {
resolveWithExistingModule(child, tree)
- done(null, child, log)
- } else if (kind === 'dep') {
- tree.missingDeps[req.name] = req.rawSpec
- done()
- } else if (kind === 'dev') {
- tree.missingDevDeps[req.name] = req.rawSpec
- done()
- } else {
- done()
+ return true
}
+ return
}
- function makeMarkable (deps, kind) {
- if (!deps) return []
- return Object.keys(deps).map(function (depname) { return { name: depname, spec: deps[depname], kind: kind } })
+ const deps = tree.package.dependencies || {}
+ for (let name of Object.keys(deps)) {
+ if (findChild(name, deps[name])) continue
+ tree.missingDeps[name] = deps[name]
+ }
+ if (tree.isTop) {
+ const devDeps = tree.package.devDependencies || {}
+ for (let name of Object.keys(devDeps)) {
+ if (findChild(name, devDeps[name])) continue
+ tree.missingDevDeps[name] = devDeps[name]
+ }
}
- // Ensure dependencies and dev dependencies are marked as required
- var tomark = makeMarkable(tree.package.dependencies, 'dep')
- if (tree.isTop) tomark = union(tomark, makeMarkable(tree.package.devDependencies, 'dev'))
-
- // Ensure any children ONLY from a shrinkwrap are also included
- var childrenOnlyInShrinkwrap = tree.children.filter(function (child) {
- return child.fromShrinkwrap &&
- !tree.package.dependencies[child.package.name] &&
- !tree.package.devDependencies[child.package.name]
- })
- var tomarkOnlyInShrinkwrap = childrenOnlyInShrinkwrap.map(function (child) {
- var name = child.package.name
- var matched = child.package._spec.match(/^@?[^@]+@(.*)$/)
- var spec = matched ? matched[1] : child.package._spec
- var kind = tree.package.dependencies[name] ? 'dep'
- : tree.package.devDependencies[name] ? 'dev'
- : 'dep'
- return { name: name, spec: spec, kind: kind }
- })
- tomark = union(tomark, tomarkOnlyInShrinkwrap)
-
- // Don't bother trying to recalc children of failed deps
- tree.children = tree.children.filter(function (child) { return !child.failed })
+ tree.children.filter((child) => !child.removed && !child.failed).forEach((child) => computeMetadata(child, seen))
- chain([
- [asyncMap, tomark, markDeps],
- [asyncMap, tree.children, function (child, done) { recalculateMetadata(child, log, seen, done) }]
- ], function () {
- tree.location = flatNameFromTree(tree)
- next(null, tree)
- })
+ return tree
}
function isDep (tree, child) {
@@ -203,12 +175,6 @@ function removeObsoleteDep (child, log) {
})
}
-function matchingDep (tree, name) {
- if (tree.package.dependencies && tree.package.dependencies[name]) return tree.package.dependencies[name]
- if (tree.package.devDependencies && tree.package.devDependencies[name]) return tree.package.devDependencies[name]
- return
-}
-
function packageRelativePath (tree) {
if (!tree) return ''
var requested = tree.package._requested || {}
@@ -216,17 +182,30 @@ function packageRelativePath (tree) {
return isLocal ? requested.fetchSpec : tree.path
}
+function matchingDep (tree, name) {
+ if (tree.package.dependencies && tree.package.dependencies[name]) return tree.package.dependencies[name]
+ if (tree.package.devDependencies && tree.package.devDependencies[name]) return tree.package.devDependencies[name]
+ return
+}
+
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) {
- return fetchMetadataWithVersion()
+ var spec = npa(arg)
+ if (spec.type !== 'file' && spec.type !== 'directory' && (spec.name == null || spec.rawSpec === '')) {
+ return fs.stat(path.join(arg, 'package.json'), (err) => {
+ if (err) {
+ var version = matchingDep(tree, spec.name)
+ if (version) {
+ return fetchPackageMetadata(npa.resolve(spec.name, version), where, done)
+ } else {
+ return fetchPackageMetadata(spec, where, done)
+ }
+ } else {
+ return fetchPackageMetadata(npa('file:' + arg), where, done)
+ }
+ })
} else {
- return fetchPackageMetadata(arg, where, done)
+ return fetchPackageMetadata(spec, where, done)
}
}, next)
}
@@ -242,11 +221,11 @@ exports.loadRequestedDeps = function (args, tree, saveToDependencies, log, next)
child.isGlobal = true
}
var childName = moduleName(child)
- child.saveSpec = computeVersionSpec(child)
+ child.saveSpec = computeVersionSpec(tree, child)
if (saveToDependencies) {
- tree.package[saveToDependencies][childName] = child.saveSpec
+ tree.package[getSaveType(tree, child)][childName] = child.saveSpec
}
- if (saveToDependencies === 'optionalDependencies') {
+ if (getSaveType(tree, child) === 'optionalDependencies') {
tree.package.dependencies[childName] = child.saveSpec
}
child.userRequired = true
@@ -262,8 +241,8 @@ exports.loadRequestedDeps = function (args, tree, saveToDependencies, log, next)
}, andForEachChild(loadDeps, andFinishTracker(log, next)))
}
-function computeVersionSpec (child) {
- validate('O', arguments)
+function computeVersionSpec (tree, child) {
+ validate('OO', arguments)
var requested = child.package._requested
if (requested.registry) {
var version = child.package.version
@@ -274,6 +253,8 @@ function computeVersionSpec (child) {
rangeDescriptor = npm.config.get('save-prefix')
}
return rangeDescriptor + version
+ } else if (requested.type === 'directory' || requested.type === 'file') {
+ return 'file:' + path.relative(tree.path, requested.fetchSpec)
} else {
return requested.saveSpec
}
@@ -295,11 +276,18 @@ exports.removeDeps = function (args, tree, saveToDependencies, log, next) {
var pkgName = moduleName(pkg)
var toRemove = tree.children.filter(moduleNameMatches(pkgName))
var pkgToRemove = toRemove[0] || createChild({package: {name: pkgName}})
- if (saveToDependencies) {
- replaceModuleByPath(tree, 'removed', pkgToRemove)
- pkgToRemove.save = saveToDependencies
+ 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)
+ }
+ pkgToRemove.requiredBy = pkgToRemove.requiredBy.filter((parent) => parent !== tree)
}
- removeObsoleteDep(pkgToRemove)
+ if (pkgToRemove.requiredBy.length === 0) removeObsoleteDep(pkgToRemove)
})
log.finish()
next()
@@ -400,8 +388,8 @@ function prefetchDeps (tree, deps, log, next) {
return npa.resolve(dep, deps[dep])
}).filter((dep) => {
return dep.registry &&
- !seen[dep.toString()] &&
- !findRequirement(tree, dep.name, dep)
+ !seen[dep.toString()] &&
+ !findRequirement(tree, dep.name, dep)
})
if (skipOptional) {
var optDeps = pkg.optionalDependencies || {}
@@ -561,13 +549,15 @@ function resolveWithNewModule (pkg, tree, log, next) {
return isInstallable(pkg, iferr(next, function () {
addBundled(pkg, iferr(next, function () {
var parent = earliestInstallable(tree, tree, pkg) || tree
+ var isLink = pkg._requested.type === 'directory'
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),
+ path: path.join(parent.isLink ? parent.realpath : parent.path, 'node_modules', pkg.name),
+ realpath: isLink ? pkg._requested.fetchSpec : path.join(parent.realpath, 'node_modules', pkg.name),
children: pkg._bundled || [],
- isLink: tree.isLink,
+ isLink: isLink,
+ isInLink: parent.isLink,
knownInstallable: true
})
delete pkg._bundled
@@ -621,7 +611,7 @@ var findRequirement = exports.findRequirement = function (tree, name, requested,
validate('OSO', [tree, name, requested])
if (!requestor) requestor = tree
var nameMatch = function (child) {
- return moduleName(child) === name && child.parent && !child.removed
+ return moduleName(child) === name && child.parent && !child.removed && !child.failed
}
var versionMatch = function (child) {
return doesChildVersionMatch(child, requested, requestor)
@@ -689,5 +679,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
+
return (earliestInstallable(requiredBy, tree.parent, pkg) || tree)
}
diff --git a/lib/install/diff-trees.js b/lib/install/diff-trees.js
index f99efab5c..67fe72d04 100644
--- a/lib/install/diff-trees.js
+++ b/lib/install/diff-trees.js
@@ -50,15 +50,6 @@ module.exports = function (oldTree, newTree, differences, log, next) {
next()
}
-function isLink (node) {
- return node && node.isLink
-}
-
-function requiredByAllLinked (node) {
- if (!node.requiredBy.length) return false
- return node.requiredBy.filter(isLink).length === node.requiredBy.length
-}
-
function isNotTopOrExtraneous (node) {
return !node.isTop && !node.userRequired && !node.existing
}
@@ -136,16 +127,9 @@ var diffTrees = module.exports._diffTrees = function (oldTree, newTree) {
Object.keys(flatNewTree).forEach(function (path) {
var pkg = flatNewTree[path]
pkg.oldPkg = flatOldTree[path]
- pkg.isInLink = (pkg.oldPkg && isLink(pkg.oldPkg.parent)) ||
- (pkg.parent && isLink(pkg.parent)) ||
- requiredByAllLinked(pkg)
if (pkg.oldPkg) {
if (!pkg.userRequired && pkgAreEquiv(pkg.oldPkg.package, pkg.package)) return
- if (!pkg.isInLink && (isLink(pkg.oldPkg) || isLink(pkg))) {
- setAction(differences, 'update-linked', pkg)
- } else {
- setAction(differences, 'update', pkg)
- }
+ setAction(differences, 'update', pkg)
} else {
var vername = getUniqueId(pkg.package)
var removing = toRemoveByUniqueId[vername] && toRemoveByUniqueId[vername].length
@@ -155,7 +139,7 @@ var diffTrees = module.exports._diffTrees = function (oldTree, newTree) {
pkg.fromPath = toRemove[flatname].path
setAction(differences, 'move', pkg)
delete toRemove[flatname]
- } else {
+ } else if (!(pkg.isInLink && pkg.fromBundle)) {
setAction(differences, 'add', pkg)
}
}
diff --git a/lib/install/filter-invalid-actions.js b/lib/install/filter-invalid-actions.js
deleted file mode 100644
index beac30b7b..000000000
--- a/lib/install/filter-invalid-actions.js
+++ /dev/null
@@ -1,36 +0,0 @@
-'use strict'
-var path = require('path')
-var validate = require('aproba')
-var log = require('npmlog')
-var packageId = require('../utils/package-id.js')
-
-module.exports = function (top, differences, next) {
- validate('SAF', arguments)
- var action
- var keep = []
-
- differences.forEach(function (action) {
- var cmd = action[0]
- var pkg = action[1]
- if (cmd === 'remove') {
- pkg.removing = true
- }
- })
-
- /*eslint no-cond-assign:0*/
- while (action = differences.shift()) {
- var cmd = action[0]
- var pkg = action[1]
- if (pkg.isInLink || (pkg.parent && (pkg.parent.target || pkg.parent.isLink))) {
- // we want to skip warning if this is a child of another module that we're removing
- if (!pkg.parent.removing) {
- log.verbose('skippingAction', 'Module is inside a symlinked module: not running ' +
- cmd + ' ' + packageId(pkg) + ' ' + path.relative(top, pkg.path))
- }
- } else {
- keep.push(action)
- }
- }
- differences.push.apply(differences, keep)
- next()
-}
diff --git a/lib/install/get-requested.js b/lib/install/get-requested.js
new file mode 100644
index 000000000..272676668
--- /dev/null
+++ b/lib/install/get-requested.js
@@ -0,0 +1,12 @@
+'use strict'
+const npa = require('npm-package-arg')
+const moduleName = require('../utils/module-name.js')
+
+module.exports = function (child) {
+ if (!child.requiredBy.length) return
+ const reqBy = child.requiredBy[0]
+ const deps = reqBy.package.dependencies || {}
+ const devDeps = reqBy.package.devDependencies || {}
+ const name = moduleName(child)
+ return npa.resolve(name, deps[name] || devDeps[name])
+}
diff --git a/lib/install/inflate-bundled.js b/lib/install/inflate-bundled.js
index bacf6d2e4..cf541106a 100644
--- a/lib/install/inflate-bundled.js
+++ b/lib/install/inflate-bundled.js
@@ -8,10 +8,10 @@ module.exports = function inflateBundled (bundler, parent, children) {
children.forEach(function (child) {
reset(child)
child.fromBundle = bundler
- child.package._inBundle = true
+ child.isInLink = bundler.isLink
child.parent = parent
child.path = childPath(parent.path, child)
- child.realpath = childPath(parent.path, child)
+ child.realpath = childPath(parent.realpath, child)
child.isLink = child.isLink || parent.isLink || parent.target
inflateBundled(bundler, child, child.children)
})
diff --git a/lib/install/inflate-shrinkwrap.js b/lib/install/inflate-shrinkwrap.js
index ec67b87d5..23b8115d7 100644
--- a/lib/install/inflate-shrinkwrap.js
+++ b/lib/install/inflate-shrinkwrap.js
@@ -59,14 +59,33 @@ function inflateShrinkwrap (topPath, tree, swdeps) {
})
}
-function inflatableChild (child, name, topPath, tree, sw, requested) {
- if (childIsEquivalent(sw, requested, child)) {
+function normalizePackageDataNoErrors (pkg) {
+ try {
+ normalizePackageData(pkg)
+ } catch (ex) {
+ // don't care
+ }
+}
+
+function inflatableChild (onDiskChild, name, topPath, tree, sw, requested) {
+ validate('OSSOOO|ZSSOOO', arguments)
+ if (onDiskChild && childIsEquivalent(sw, requested, onDiskChild)) {
// The version on disk matches the shrinkwrap entry.
- if (!child.fromShrinkwrap) child.fromShrinkwrap = requested.raw
- if (sw.dev) child.shrinkwrapDev = true
- tree.children.push(child)
- annotateMetadata(child.package, requested, requested.raw, topPath)
- return BB.resolve(child)
+ if (!onDiskChild.fromShrinkwrap) onDiskChild.fromShrinkwrap = true
+ if (sw.dev) onDiskChild.shrinkwrapDev = true
+ onDiskChild.package._requested = requested
+ onDiskChild.package._spec = requested.rawSpec
+ onDiskChild.package._where = topPath
+ onDiskChild.fromBundle = sw.bundled ? tree.fromBundle || tree : null
+ if (!onDiskChild.package._args) onDiskChild.package._args = []
+ onDiskChild.package._args.push([String(requested), topPath])
+ // non-npm registries can and will return unnormalized data, plus
+ // even the npm registry may have package data normalized with older
+ // normalization rules. This ensures we get package data in a consistent,
+ // stable format.
+ normalizePackageDataNoErrors(onDiskChild.package)
+ tree.children.push(onDiskChild)
+ return BB.resolve(onDiskChild)
} else if (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.
@@ -85,10 +104,13 @@ function makeFakeChild (name, topPath, tree, sw, requested) {
name: name,
version: sw.version,
_resolved: sw.resolved,
+ _requested: requested,
_optional: sw.optional,
- _inBundle: sw.bundled,
_integrity: sw.integrity,
- _from: from
+ _from: from,
+ _spec: requested.rawSpec,
+ _where: topPath,
+ _args: [[requested.toString(), topPath]]
}
let bundleAdded = BB.resolve()
if (Object.keys(sw.dependencies || {}).some((d) => {
@@ -103,11 +125,13 @@ function makeFakeChild (name, topPath, tree, sw, requested) {
loaded: true,
parent: tree,
children: pkg._bundled || [],
- fromShrinkwrap: pkg._requested,
+ fromShrinkwrap: true,
+ fromBundle: sw.bundled ? tree.fromBundle || tree : null,
path: childPath(tree.path, pkg),
- realpath: childPath(tree.realpath, pkg)
+ realpath: childPath(tree.realpath, pkg),
+ location: tree.location + '/' + pkg.name,
+ isInLink: tree.isLink
})
- annotateMetadata(child.package, requested, requested.raw, topPath)
tree.children.push(child)
if (pkg._bundled) {
delete pkg._bundled
@@ -125,14 +149,18 @@ function fetchChild (topPath, tree, sw, requested) {
pkg._optional = optional
return addBundled(pkg).then(() => pkg)
}).then((pkg) => {
+ var isLink = pkg._requested.type === 'directory'
const child = createChild({
package: pkg,
loaded: true,
parent: tree,
- fromShrinkwrap: pkg._requested,
+ fromShrinkwrap: requested,
path: childPath(tree.path, pkg),
- realpath: childPath(tree.realpath, pkg),
- children: pkg._bundled || []
+ realpath: isLink ? requested.fetchSpec : childPath(tree.realpath, pkg),
+ children: pkg._bundled || [],
+ location: tree.location + '/' + pkg.name,
+ isLink: isLink,
+ isInLink: tree.isLink
})
tree.children.push(child)
if (pkg._bundled) {
@@ -150,22 +178,3 @@ function childIsEquivalent (sw, requested, child) {
if (!requested.registry && sw.from) return child.package._from === sw.from
return child.package.version === sw.version
}
-
-module.exports.annotateMetadata = annotateMetadata
-function annotateMetadata (pkg, requested, spec, where) {
- validate('OOSS', arguments)
- pkg._requested = requested
- pkg._spec = spec
- pkg._where = where
- if (!pkg._args) pkg._args = []
- pkg._args.push([requested, where])
- // non-npm registries can and will return unnormalized data, plus
- // even the npm registry may have package data normalized with older
- // normalization rules. This ensures we get package data in a consistent,
- // stable format.
- try {
- normalizePackageData(pkg)
- } catch (ex) {
- // don't care
- }
-}
diff --git a/lib/install/node.js b/lib/install/node.js
index 64414659d..305f705ef 100644
--- a/lib/install/node.js
+++ b/lib/install/node.js
@@ -18,10 +18,10 @@ var defaultTemplate = {
realpath: null,
location: null,
userRequired: false,
- existing: false,
save: false,
saveSpec: null,
- isTop: false
+ isTop: false,
+ fromBundle: false
}
function isLink (node) {
@@ -38,8 +38,21 @@ var create = exports.create = function (node, template) {
if (node[key] != null) return
node[key] = template[key]
})
- if (isLink(node.parent)) {
- node.isLink = true
+ if (node.package) {
+ // isLink is true for the symlink and everything inside it.
+ // by contrast, isInLink is true for only the things inside a link
+ if (node.isLink == null && isLink(node.parent)) {
+ node.isLink = true
+ node.isInLink = true
+ } else if (node.isLink == null) {
+ node.isLink = false
+ node.isInLink = false
+ }
+ if (node.fromBundle == null && node.package) {
+ node.fromBundle = node.package._inBundle
+ } else if (node.fromBundle == null) {
+ node.fromBundle = false
+ }
}
return node
}
diff --git a/lib/install/read-shrinkwrap.js b/lib/install/read-shrinkwrap.js
index f881a46ca..bf941a49c 100644
--- a/lib/install/read-shrinkwrap.js
+++ b/lib/install/read-shrinkwrap.js
@@ -17,7 +17,7 @@ function readShrinkwrap (child, next) {
BB.join(
readLockfile('npm-shrinkwrap.json', child),
// Don't read non-root lockfiles
- !child.parent && readLockfile('package-lock.json', child),
+ child.isTop && readLockfile('package-lock.json', child),
(shrinkwrap, lockfile) => {
if (shrinkwrap && lockfile) {
log.warn('read-shrinkwrap', 'Ignoring package-lock.json because there is already an npm-shrinkwrap.json. Please use only one of the two.')
diff --git a/lib/install/realize-shrinkwrap-specifier.js b/lib/install/realize-shrinkwrap-specifier.js
index 0a2423b0c..91030bfa8 100644
--- a/lib/install/realize-shrinkwrap-specifier.js
+++ b/lib/install/realize-shrinkwrap-specifier.js
@@ -2,13 +2,17 @@
var npa = require('npm-package-arg')
module.exports = function (name, sw, where) {
- if (sw.resolved) {
- return npa.resolve(name, sw.resolved, where)
- } else if (sw.from) {
- try {
+ try {
+ if (sw.version && sw.integrity) {
+ return npa.resolve(name, sw.version, where)
+ }
+ if (sw.resolved) {
+ return npa.resolve(name, sw.resolved, where)
+ }
+ if (sw.from) {
var spec = npa(sw.from, where)
if (!spec.registry) return spec
- } catch (_) { }
- }
+ }
+ } catch (_) { }
return npa.resolve(name, sw.version, where)
}
diff --git a/lib/install/save.js b/lib/install/save.js
index f9d57aa2b..5d5f4e7f7 100644
--- a/lib/install/save.js
+++ b/lib/install/save.js
@@ -43,7 +43,7 @@ function andWarnErrors (cb) {
function saveShrinkwrap (tree, next) {
validate('OF', arguments)
- createShrinkwrap(tree.path, tree.package, {silent: false}, next)
+ createShrinkwrap(tree, {silent: false}, next)
}
function savePackageJson (args, tree, next) {
@@ -61,31 +61,31 @@ function savePackageJson (args, tree, next) {
fs.readFile(saveTarget, 'utf8', iferr(next, function (packagejson) {
const indent = detectIndent(packagejson).indent || ' '
try {
- packagejson = parseJSON(packagejson)
+ tree.package = parseJSON(packagejson)
} catch (ex) {
return next(ex)
}
// If we're saving bundled deps, normalize the key before we start
if (saveBundle) {
- var bundle = packagejson.bundleDependencies || packagejson.bundledDependencies
- delete packagejson.bundledDependencies
+ var bundle = tree.package.bundleDependencies || tree.package.bundledDependencies
+ delete tree.package.bundledDependencies
if (!Array.isArray(bundle)) bundle = []
}
var toSave = getThingsToSave(tree)
- var toRemove = getThingsToRemove(args, tree)
+ var toRemove = getThingsToRemove(tree)
var savingTo = {}
toSave.forEach(function (pkg) { savingTo[pkg.save] = true })
toRemove.forEach(function (pkg) { savingTo[pkg.save] = true })
Object.keys(savingTo).forEach(function (save) {
- if (!packagejson[save]) packagejson[save] = {}
+ if (!tree.package[save]) tree.package[save] = {}
})
log.verbose('saving', toSave)
toSave.forEach(function (pkg) {
- packagejson[pkg.save][pkg.name] = pkg.spec
+ tree.package[pkg.save][pkg.name] = pkg.spec
if (saveBundle) {
var ii = bundle.indexOf(pkg.name)
if (ii === -1) bundle.push(pkg.name)
@@ -93,35 +93,47 @@ function savePackageJson (args, tree, next) {
})
toRemove.forEach(function (pkg) {
- delete packagejson[pkg.save][pkg.name]
+ delete tree.package[pkg.save][pkg.name]
if (saveBundle) {
bundle = without(bundle, pkg.name)
}
})
Object.keys(savingTo).forEach(function (key) {
- packagejson[key] = deepSortObject(packagejson[key])
+ tree.package[key] = deepSortObject(tree.package[key])
})
if (saveBundle) {
- packagejson.bundledDependencies = deepSortObject(bundle)
+ tree.package.bundleDependencies = deepSortObject(bundle)
}
- var json = JSON.stringify(packagejson, null, indent) + '\n'
+ var json = JSON.stringify(tree.package, null, indent) + '\n'
writeFileAtomic(saveTarget, json, next)
}))
}
-var getSaveType = exports.getSaveType = function (args) {
- validate('A', arguments)
+exports.getSaveType = function (tree, arg) {
+ if (arguments.length) validate('OO', arguments)
var globalInstall = npm.config.get('global')
var noSaveFlags = !npm.config.get('save') &&
!npm.config.get('save-dev') &&
!npm.config.get('save-optional')
if (globalInstall || noSaveFlags) return null
- if (npm.config.get('save-optional')) return 'optionalDependencies'
- else if (npm.config.get('save-dev')) return 'devDependencies'
- else return 'dependencies'
+ if (npm.config.get('save-optional')) {
+ return 'optionalDependencies'
+ } else if (npm.config.get('save-dev')) {
+ return 'devDependencies'
+ } else {
+ if (arg) {
+ var name = moduleName(arg)
+ if (tree.package.optionalDependencies[name]) {
+ return 'optionalDependencies'
+ } else if (tree.package.devDependencies[name]) {
+ return 'devDependencies'
+ }
+ }
+ return 'dependencies'
+ }
}
function getThingsToSave (tree) {
@@ -138,8 +150,8 @@ function getThingsToSave (tree) {
return toSave
}
-function getThingsToRemove (args, tree) {
- validate('AO', arguments)
+function getThingsToRemove (tree) {
+ validate('O', arguments)
if (!tree.removed) return []
var toRemove = tree.removed.map(function (child) {
return {
@@ -147,12 +159,5 @@ function getThingsToRemove (args, tree) {
save: child.save
}
})
- var saveType = getSaveType(args)
- args.forEach(function (arg) {
- toRemove.push({
- name: arg,
- save: saveType
- })
- })
return toRemove
}
diff --git a/lib/install/update-package-json.js b/lib/install/update-package-json.js
index eee530c3c..21b88bead 100644
--- a/lib/install/update-package-json.js
+++ b/lib/install/update-package-json.js
@@ -27,13 +27,13 @@ module.exports = function (mod, buildpath, next) {
}
})
.concat(mod.userRequired ? ['#USER'] : [])
- .concat(mod.existing ? ['#EXISTING'] : [])
.sort()
pkg._location = mod.location
pkg._phantomChildren = {}
Object.keys(mod.phantomChildren).sort().forEach(function (name) {
pkg._phantomChildren[name] = mod.phantomChildren[name].package.version
})
+ pkg._inBundle = !!mod.fromBundle
// sort keys that are known safe to sort to produce more consistent output
sortKeys.forEach(function (key) {
diff --git a/lib/ls.js b/lib/ls.js
index 478b914bb..b993dd623 100644
--- a/lib/ls.js
+++ b/lib/ls.js
@@ -9,7 +9,6 @@ module.exports = exports = ls
var path = require('path')
var url = require('url')
var readPackageTree = require('read-package-tree')
-var log = require('npmlog')
var archy = require('archy')
var semver = require('semver')
var color = require('ansicolors')
@@ -19,7 +18,7 @@ var sortedObject = require('sorted-object')
var extend = Object.assign || require('util')._extend
var npm = require('./npm.js')
var mutateIntoLogicalTree = require('./install/mutate-into-logical-tree.js')
-var recalculateMetadata = require('./install/deps.js').recalculateMetadata
+var computeMetadata = require('./install/deps.js').computeMetadata
var packageId = require('./utils/package-id.js')
var usage = require('./utils/usage')
var output = require('./utils/output.js')
@@ -37,14 +36,14 @@ function ls (args, silent, cb) {
silent = false
}
var dir = path.resolve(npm.dir, '..')
- readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (physicalTree) {
+ readPackageTree(dir, andComputeMetadata(iferr(cb, function (physicalTree) {
lsFromTree(dir, physicalTree, args, silent, cb)
})))
}
-function andRecalculateMetadata (next) {
+function andComputeMetadata (next) {
return function (er, tree) {
- recalculateMetadata(tree || {}, log, next)
+ next(null, computeMetadata(tree || {}))
}
}
diff --git a/lib/outdated.js b/lib/outdated.js
index 01cf39d10..7d5cfba86 100644
--- a/lib/outdated.js
+++ b/lib/outdated.js
@@ -23,7 +23,6 @@ outdated.completion = require('./utils/completion/installed-deep.js')
var os = require('os')
var url = require('url')
var path = require('path')
-var log = require('npmlog')
var readPackageTree = require('read-package-tree')
var readJson = require('read-package-json')
var asyncMap = require('slide').asyncMap
@@ -38,7 +37,7 @@ var npm = require('./npm.js')
var long = npm.config.get('long')
var mapToRegistry = require('./utils/map-to-registry.js')
var isExtraneous = require('./install/is-extraneous.js')
-var recalculateMetadata = require('./install/deps.js').recalculateMetadata
+var computeMetadata = require('./install/deps.js').computeMetadata
var moduleName = require('./utils/module-name.js')
var output = require('./utils/output.js')
var ansiTrim = require('./utils/ansi-trim')
@@ -59,10 +58,10 @@ function uniq (list) {
return uniqed
}
-function andRecalculateMetadata (next) {
+function andComputeMetadata (next) {
return function (er, tree) {
if (er) return next(er)
- recalculateMetadata(tree, log, next)
+ next(null, computeMetadata(tree))
}
}
@@ -76,7 +75,7 @@ function outdated (args, silent, cb) {
// default depth for `outdated` is 0 (cf. `ls`)
if (npm.config.get('depth') === Infinity) npm.config.set('depth', 0)
- readPackageTree(dir, andRecalculateMetadata(function (er, tree) {
+ readPackageTree(dir, andComputeMetadata(function (er, tree) {
if (!tree) return cb(er)
mutateIntoLogicalTree(tree)
outdated_(args, '', tree, {}, 0, function (er, list) {
diff --git a/lib/prune.js b/lib/prune.js
index a590457a1..39d1c8ffb 100644
--- a/lib/prune.js
+++ b/lib/prune.js
@@ -15,6 +15,7 @@ var isDev = require('./install/is-dev-dep.js')
var removeDeps = require('./install/deps.js').removeDeps
var loadExtraneous = require('./install/deps.js').loadExtraneous
var chain = require('slide').chain
+var computeMetadata = require('./install/deps.js').computeMetadata
prune.completion = require('./utils/completion/installed-deep.js')
@@ -34,6 +35,7 @@ Pruner.prototype.loadAllDepsIntoIdealTree = function (cb) {
var cg = this.progress['loadIdealTree:loadAllDepsIntoIdealTree']
var steps = []
+ computeMetadata(this.idealTree)
var self = this
var excludeDev = npm.config.get('production') || /^prod(uction)?$/.test(npm.config.get('only'))
function shouldPrune (child) {
@@ -54,7 +56,7 @@ Pruner.prototype.loadAllDepsIntoIdealTree = function (cb) {
function nameObj (name) {
return {name: name}
}
- var toPrune = this.currentTree.children.filter(shouldPrune).map(getModuleName).filter(matchesArg).map(nameObj)
+ var toPrune = this.idealTree.children.filter(shouldPrune).map(getModuleName).filter(matchesArg).map(nameObj)
steps.push(
[removeDeps, toPrune, this.idealTree, null, cg.newGroup('removeDeps')],
diff --git a/lib/shrinkwrap.js b/lib/shrinkwrap.js
index 5cbba2137..eeac91f24 100644
--- a/lib/shrinkwrap.js
+++ b/lib/shrinkwrap.js
@@ -17,16 +17,14 @@ const move = require('move-concurrently')
const npm = require('./npm.js')
const packageId = require('./utils/package-id.js')
const path = require('path')
-const readPackageJson = require('read-package-json')
const readPackageTree = require('read-package-tree')
-const recalculateMetadata = require('./install/deps.js').recalculateMetadata
const ssri = require('ssri')
const validate = require('aproba')
-const validatePeerDeps = require('./install/deps.js').validatePeerDeps
+const id = require('./install/deps.js')
const writeFileAtomic = require('write-file-atomic')
-
const SHRINKWRAP = 'npm-shrinkwrap.json'
const PKGLOCK = 'package-lock.json'
+const getRequested = require('./install/get-requested.js')
// emit JSON describing versions of all packages currently installed (for later
// use with shrinkwrap install)
@@ -43,8 +41,6 @@ function shrinkwrap (args, silent, cb) {
log.warn('shrinkwrap', "doesn't take positional args")
}
- var packagePath = path.join(npm.localPrefix, 'package.json')
-
move(
path.resolve(npm.prefix, PKGLOCK),
path.resolve(npm.prefix, SHRINKWRAP),
@@ -53,42 +49,40 @@ function shrinkwrap (args, silent, cb) {
log.notice('', `${PKGLOCK} has been renamed to ${SHRINKWRAP}. ${SHRINKWRAP} will be used for future installations.`)
return fs.readFileAsync(path.resolve(npm.prefix, SHRINKWRAP)).then((d) => {
return JSON.parse(d)
- })
+ }).then(() => cb(), cb)
}).catch({code: 'ENOENT'}, () => {
- readPackageJson(packagePath, iferr(cb, function (pkg) {
- createShrinkwrap(npm.localPrefix, pkg, {
+ readPackageTree(npm.localPrefix, andComputeMetadata(iferr(cb, function (tree) {
+ createShrinkwrap(tree, {
silent,
defaultFile: SHRINKWRAP
}, cb)
- }))
- }).then((data) => cb(null, data), cb)
+ })))
+ })
}
module.exports.createShrinkwrap = createShrinkwrap
-function createShrinkwrap (dir, pkg, opts, cb) {
+function createShrinkwrap (tree, opts, cb) {
opts = opts || {}
- lifecycle(pkg, 'preshrinkwrap', dir, function () {
- readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (tree) {
- var pkginfo = treeToShrinkwrap(tree)
-
- chain([
- [lifecycle, tree.package, 'shrinkwrap', dir],
- [shrinkwrap_, dir, pkginfo, opts],
- [lifecycle, tree.package, 'postshrinkwrap', dir]
- ], iferr(cb, function (data) {
- cb(null, data[0])
- }))
- })))
+ lifecycle(tree.package, 'preshrinkwrap', tree.path, function () {
+ var pkginfo = treeToShrinkwrap(tree)
+
+ chain([
+ [lifecycle, tree.package, 'shrinkwrap', tree.path],
+ [shrinkwrap_, tree.path, pkginfo, opts],
+ [lifecycle, tree.package, 'postshrinkwrap', tree.path]
+ ], iferr(cb, function (data) {
+ cb(null, data[0])
+ }))
})
}
-function andRecalculateMetadata (next) {
+function andComputeMetadata (next) {
validate('F', arguments)
return function (er, tree) {
validate('EO', arguments)
if (er) return next(er)
- recalculateMetadata(tree, log, next)
+ next(null, id.computeMetadata(tree))
}
}
@@ -99,14 +93,14 @@ function treeToShrinkwrap (tree) {
if (tree.package.version) pkginfo.version = tree.package.version
var problems = []
if (tree.children.length) {
- shrinkwrapDeps(problems, pkginfo.dependencies = {}, tree)
+ shrinkwrapDeps(problems, pkginfo.dependencies = {}, tree, tree)
}
if (problems.length) pkginfo.problems = problems
return pkginfo
}
-function shrinkwrapDeps (problems, deps, tree, seen) {
- validate('AOO', [problems, deps, tree])
+function shrinkwrapDeps (problems, deps, top, tree, seen) {
+ validate('AOOO', [problems, deps, top, tree])
if (!seen) seen = {}
if (seen[tree.path]) return
seen[tree.path] = true
@@ -123,11 +117,20 @@ function shrinkwrapDeps (problems, deps, tree, seen) {
tree.children.sort(function (aa, bb) { return moduleName(aa).localeCompare(moduleName(bb)) }).forEach(function (child) {
var childIsOnlyDev = isOnlyDev(child)
var pkginfo = deps[moduleName(child)] = {}
- pkginfo.version = child.package.version
- if (child.package._inBundle) {
+ var req = child.package._requested || getRequested(child)
+ if (req.type === 'directory' || req.type === 'file') {
+ pkginfo.version = 'file:' + path.relative(top.path, child.package._resolved || req.fetchSpec)
+ } else if (!req.registry) {
+ pkginfo.version = child.package._resolved
+ } else {
+ pkginfo.version = child.package.version
+ }
+ if (child.fromBundle || child.isInLink) {
pkginfo.bundled = true
} else {
- pkginfo.resolved = child.package._resolved
+ if (req.registry) {
+ pkginfo.resolved = child.package._resolved
+ }
pkginfo.integrity = child.package._integrity
if (!pkginfo.integrity && child.package._shasum) {
pkginfo.integrity = ssri.fromHex(child.package._shasum, 'sha1')
@@ -138,13 +141,13 @@ function shrinkwrapDeps (problems, deps, tree, seen) {
if (isExtraneous(child)) {
problems.push('extraneous: ' + child.package._id + ' ' + child.path)
}
- validatePeerDeps(child, function (tree, pkgname, version) {
+ 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, child, seen)
+ shrinkwrapDeps(problems, pkginfo.dependencies, top, child, seen)
}
})
}
diff --git a/lib/uninstall.js b/lib/uninstall.js
index 802cf7a28..9e3d91ac4 100644
--- a/lib/uninstall.js
+++ b/lib/uninstall.js
@@ -64,11 +64,10 @@ Uninstaller.prototype.loadArgMetadata = function (next) {
Uninstaller.prototype.loadAllDepsIntoIdealTree = function (cb) {
validate('F', arguments)
log.silly('uninstall', 'loadAllDepsIntoIdealTree')
- var saveDeps = getSaveType(this.args)
+ var 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')])
diff --git a/lib/utils/tar.js b/lib/utils/tar.js
index d88c8d532..7ebc9d687 100644
--- a/lib/utils/tar.js
+++ b/lib/utils/tar.js
@@ -66,14 +66,11 @@ function pack (tarball, folder, pkg, cb) {
// we require this at runtime due to load-order issues, because recursive
// requires fail if you replace the exports object, and we do, not in deps, but
// in a dep of it.
- var recalculateMetadata = require('../install/deps.js').recalculateMetadata
+ var computeMetadata = require('../install/deps.js').computeMetadata
readPackageTree(folder, pulseTillDone('pack:readTree:' + packageId(pkg), iferr(cb, function (tree) {
- var recalcGroup = log.newGroup('pack:recalc:' + packageId(pkg))
- recalculateMetadata(tree, recalcGroup, iferr(cb, function () {
- recalcGroup.finish()
- pack_(tarball, folder, tree, pkg, pulseTillDone('pack:' + packageId(pkg), cb))
- }))
+ computeMetadata(tree)
+ pack_(tarball, folder, tree, pkg, pulseTillDone('pack:' + packageId(pkg), cb))
})))
}
})