summaryrefslogtreecommitdiff
path: root/deps/npm/lib/shrinkwrap.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/lib/shrinkwrap.js')
-rw-r--r--deps/npm/lib/shrinkwrap.js92
1 files changed, 73 insertions, 19 deletions
diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js
index c6a41842fc..02481a235e 100644
--- a/deps/npm/lib/shrinkwrap.js
+++ b/deps/npm/lib/shrinkwrap.js
@@ -15,11 +15,13 @@ var npm = require('./npm.js')
var recalculateMetadata = require('./install/deps.js').recalculateMetadata
var validatePeerDeps = require('./install/deps.js').validatePeerDeps
var isExtraneous = require('./install/is-extraneous.js')
-var isOnlyDev = require('./install/is-dev.js').isOnlyDev
var packageId = require('./utils/package-id.js')
var moduleName = require('./utils/module-name.js')
var output = require('./utils/output.js')
var lifecycle = require('./utils/lifecycle.js')
+var isDevDep = require('./install/is-dev-dep.js')
+var isProdDep = require('./install/is-prod-dep.js')
+var isOptDep = require('./install/is-opt-dep.js')
shrinkwrap.usage = 'npm shrinkwrap'
@@ -35,25 +37,31 @@ function shrinkwrap (args, silent, cb) {
var dir = path.resolve(npm.dir, '..')
var packagePath = path.join(npm.localPrefix, 'package.json')
- npm.config.set('production', true)
-
- readPackageJson(packagePath, iferr(cb, function (data) {
- lifecycle(data, 'preshrinkwrap', function () {
- readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (tree) {
- var pkginfo = treeToShrinkwrap(tree, !!npm.config.get('dev') || /^dev(elopment)?$/.test(npm.config.get('also')))
-
- chain([
- [lifecycle, tree.package, 'shrinkwrap'],
- [shrinkwrap_, pkginfo, silent],
- [lifecycle, tree.package, 'postshrinkwrap']
- ], iferr(cb, function (data) {
- cb(null, data[0])
- }))
- })))
- })
+ var dev = !!npm.config.get('dev') || /^dev(elopment)?$/.test(npm.config.get('also'))
+
+ readPackageJson(packagePath, iferr(cb, function (pkg) {
+ createShrinkwrap(dir, pkg, dev, silent, cb)
}))
}
+module.exports.createShrinkwrap = createShrinkwrap
+
+function createShrinkwrap (dir, pkg, dev, silent, cb) {
+ lifecycle(pkg, 'preshrinkwrap', function () {
+ readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (tree) {
+ var pkginfo = treeToShrinkwrap(tree, dev)
+
+ chain([
+ [lifecycle, tree.package, 'shrinkwrap'],
+ [shrinkwrap_, pkginfo, silent],
+ [lifecycle, tree.package, 'postshrinkwrap']
+ ], iferr(cb, function (data) {
+ cb(null, data[0])
+ }))
+ })))
+ })
+}
+
function andRecalculateMetadata (next) {
validate('F', arguments)
return function (er, tree) {
@@ -92,14 +100,17 @@ function shrinkwrapDeps (dev, problems, deps, tree, seen) {
}
})
tree.children.sort(function (aa, bb) { return moduleName(aa).localeCompare(moduleName(bb)) }).forEach(function (child) {
- if (!dev && isOnlyDev(child)) {
- log.warn('shrinkwrap', 'Excluding devDependency: %s', packageId(child), child.parent.package.dependencies)
+ var childIsOnlyDev = isOnlyDev(child)
+ if (!dev && childIsOnlyDev) {
+ log.warn('shrinkwrap', 'Excluding devDependency: %s', child.location)
return
}
var pkginfo = deps[moduleName(child)] = {}
pkginfo.version = child.package.version
pkginfo.from = child.package._from
pkginfo.resolved = child.package._resolved
+ if (dev && childIsOnlyDev) pkginfo.dev = true
+ if (isOptional(child)) pkginfo.optional = true
if (isExtraneous(child)) {
problems.push('extraneous: ' + child.package._id + ' ' + child.path)
}
@@ -143,3 +154,46 @@ function save (pkginfo, silent, cb) {
cb(null, pkginfo)
})
}
+
+// Returns true if the module `node` is only required direcctly as a dev
+// dependency of the top level or transitively _from_ top level dev
+// dependencies.
+// Dual mode modules (that are both dev AND prod) should return false.
+function isOnlyDev (node, seen) {
+ if (!seen) seen = {}
+ return node.requiredBy.length && node.requiredBy.every(andIsOnlyDev(moduleName(node), seen))
+}
+
+// There is a known limitation with this implementation: If a dependency is
+// ONLY required by cycles that are detached from the top level then it will
+// ultimately return ture.
+//
+// This is ok though: We don't allow shrinkwraps with extraneous deps and
+// these situation is caught by the extraneous checker before we get here.
+function andIsOnlyDev (name, seen) {
+ return function (req) {
+ var isDev = isDevDep(req, name)
+ var isProd = isProdDep(req, name)
+ if (req.isTop) {
+ return isDev && !isProd
+ } else {
+ if (seen[req.path]) return true
+ seen[req.path] = true
+ return isOnlyDev(req, seen)
+ }
+ }
+}
+
+function isOptional (node, seen) {
+ if (!seen) seen = {}
+ // If a node is not required by anything, then we've reached
+ // the top level package.
+ if (seen[node.path] || node.requiredBy.length === 0) {
+ return false
+ }
+ seen[node.path] = true
+
+ return node.requiredBy.every(function (req) {
+ return isOptDep(req, node.package.name) || isOptional(req, seen)
+ })
+}