diff options
Diffstat (limited to 'deps/npm/lib/install/mutate-into-logical-tree.js')
-rw-r--r-- | deps/npm/lib/install/mutate-into-logical-tree.js | 71 |
1 files changed, 46 insertions, 25 deletions
diff --git a/deps/npm/lib/install/mutate-into-logical-tree.js b/deps/npm/lib/install/mutate-into-logical-tree.js index 833aa94c940..b2059da9060 100644 --- a/deps/npm/lib/install/mutate-into-logical-tree.js +++ b/deps/npm/lib/install/mutate-into-logical-tree.js @@ -8,6 +8,24 @@ var validateAllPeerDeps = require('./deps.js').validateAllPeerDeps var packageId = require('../utils/package-id.js') var moduleName = require('../utils/module-name.js') +// Return true if tree is a part of a cycle that: +// A) Never connects to the top of the tree +// B) Has not not had a point in the cycle arbitraryly declared its top +// yet. +function isDisconnectedCycle (tree, seen) { + if (!seen) seen = {} + if (tree.isTop || tree.cycleTop || tree.requiredBy.length === 0) { + return false + } else if (seen[tree.path]) { + return true + } else { + seen[tree.path] = true + return tree.requiredBy.every(function (node) { + return isDisconnectedCycle(node, Object.create(seen)) + }) + } +} + var mutateIntoLogicalTree = module.exports = function (tree) { validate('O', arguments) @@ -18,35 +36,29 @@ var mutateIntoLogicalTree = module.exports = function (tree) { var flat = flattenTree(tree) - function getNode (flatname) { - return flatname.substr(0, 5) === '#DEV:' - ? flat[flatname.substr(5)] - : flat[flatname] - } - Object.keys(flat).sort().forEach(function (flatname) { var node = flat[flatname] - var requiredBy = node.package._requiredBy || [] - var requiredByNames = requiredBy.filter(function (parentFlatname) { - var parentNode = getNode(parentFlatname) - if (!parentNode) return false - return parentNode.package.dependencies[moduleName(node)] || - (parentNode.package.devDependencies && parentNode.package.devDependencies[moduleName(node)]) - }) - requiredBy = requiredByNames.map(getNode) - - node.requiredBy = requiredBy - - if (!requiredBy.length) return + if (!node.requiredBy.length) return - if (node.parent) node.parent.children = without(node.parent.children, node) + if (node.parent) { + // If a node is a cycle that never reaches the root of the logical + // tree then we'll leave it attached to the root, or else it + // would go missing. Further we'll note that this is the node in the + // cycle that we picked arbitrarily to be the one attached to the root. + // others will fall + if (isDisconnectedCycle(node)) { + node.cycleTop = true + // Nor do we want to disconnect non-cyclical extraneous modules from the tree. + } else if (node.requiredBy.length) { + // regular deps though, we do, as we're moving them into the capable + // hands of the modules that require them. + node.parent.children = without(node.parent.children, node) + } + } - requiredBy.forEach(function (parentNode) { + node.requiredBy.forEach(function (parentNode) { parentNode.children = union(parentNode.children, [node]) }) - if (node.package._requiredBy.some(function (nodename) { return nodename[0] === '#' })) { - tree.children = union(tree.children, [node]) - } }) return tree } @@ -70,18 +82,27 @@ function translateTree_ (tree, seen) { tree.children.forEach(function (child) { pkg.dependencies[moduleName(child)] = translateTree_(child, seen) }) - Object.keys(tree.missingDeps).forEach(function (name) { + + function markMissing (name, requiredBy) { if (pkg.dependencies[name]) { + if (pkg.dependencies[name].missing) return pkg.dependencies[name].invalid = true pkg.dependencies[name].realName = name pkg.dependencies[name].extraneous = false } else { pkg.dependencies[name] = { - requiredBy: tree.missingDeps[name], + requiredBy: requiredBy, missing: true, optional: !!pkg.optionalDependencies[name] } } + } + + Object.keys(tree.missingDeps).forEach(function (name) { + markMissing(name, tree.missingDeps[name]) + }) + Object.keys(tree.missingDevDeps).forEach(function (name) { + markMissing(name, tree.missingDevDeps[name]) }) var checkForMissingPeers = (tree.parent ? [] : [tree]).concat(tree.children) checkForMissingPeers.filter(function (child) { |