mirror of
https://github.com/nodejs/node.git
synced 2025-05-13 19:50:17 +00:00

PR-URL: https://github.com/nodejs/node/pull/40865 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
116 lines
3.1 KiB
JavaScript
116 lines
3.1 KiB
JavaScript
const { depth } = require('treeverse')
|
|
|
|
const calcDepFlags = (tree, resetRoot = true) => {
|
|
if (resetRoot) {
|
|
tree.dev = false
|
|
tree.optional = false
|
|
tree.devOptional = false
|
|
tree.peer = false
|
|
}
|
|
const ret = depth({
|
|
tree,
|
|
visit: node => calcDepFlagsStep(node),
|
|
filter: node => node,
|
|
getChildren: (node, tree) =>
|
|
[...tree.edgesOut.values()].map(edge => edge.to),
|
|
})
|
|
return ret
|
|
}
|
|
|
|
const calcDepFlagsStep = (node) => {
|
|
// This rewalk is necessary to handle cases where devDep and optional
|
|
// or normal dependency graphs overlap deep in the dep graph.
|
|
// Since we're only walking through deps that are not already flagged
|
|
// as non-dev/non-optional, it's typically a very shallow traversal
|
|
node.extraneous = false
|
|
resetParents(node, 'extraneous')
|
|
resetParents(node, 'dev')
|
|
resetParents(node, 'peer')
|
|
resetParents(node, 'devOptional')
|
|
resetParents(node, 'optional')
|
|
|
|
// for links, map their hierarchy appropriately
|
|
if (node.isLink) {
|
|
node.target.dev = node.dev
|
|
node.target.optional = node.optional
|
|
node.target.devOptional = node.devOptional
|
|
node.target.peer = node.peer
|
|
return calcDepFlagsStep(node.target)
|
|
}
|
|
|
|
node.edgesOut.forEach(({ peer, optional, dev, to }) => {
|
|
// if the dep is missing, then its flags are already maximally unset
|
|
if (!to) {
|
|
return
|
|
}
|
|
|
|
// everything with any kind of edge into it is not extraneous
|
|
to.extraneous = false
|
|
|
|
// devOptional is the *overlap* of the dev and optional tree.
|
|
// however, for convenience and to save an extra rewalk, we leave
|
|
// it set when we are in *either* tree, and then omit it from the
|
|
// package-lock if either dev or optional are set.
|
|
const unsetDevOpt = !node.devOptional && !node.dev && !node.optional &&
|
|
!dev && !optional
|
|
|
|
// if we are not in the devOpt tree, then we're also not in
|
|
// either the dev or opt trees
|
|
const unsetDev = unsetDevOpt || !node.dev && !dev
|
|
const unsetOpt = unsetDevOpt ||
|
|
!node.optional && !optional
|
|
const unsetPeer = !node.peer && !peer
|
|
|
|
if (unsetPeer) {
|
|
unsetFlag(to, 'peer')
|
|
}
|
|
|
|
if (unsetDevOpt) {
|
|
unsetFlag(to, 'devOptional')
|
|
}
|
|
|
|
if (unsetDev) {
|
|
unsetFlag(to, 'dev')
|
|
}
|
|
|
|
if (unsetOpt) {
|
|
unsetFlag(to, 'optional')
|
|
}
|
|
})
|
|
|
|
return node
|
|
}
|
|
|
|
const resetParents = (node, flag) => {
|
|
if (node[flag]) {
|
|
return
|
|
}
|
|
|
|
for (let p = node; p && (p === node || p[flag]); p = p.resolveParent) {
|
|
p[flag] = false
|
|
}
|
|
}
|
|
|
|
// typically a short walk, since it only traverses deps that
|
|
// have the flag set.
|
|
const unsetFlag = (node, flag) => {
|
|
if (node[flag]) {
|
|
node[flag] = false
|
|
depth({
|
|
tree: node,
|
|
visit: node => {
|
|
node.extraneous = node[flag] = false
|
|
if (node.isLink) {
|
|
node.target.extraneous = node.target[flag] = false
|
|
}
|
|
},
|
|
getChildren: node => [...node.target.edgesOut.values()]
|
|
.filter(edge => edge.to && edge.to[flag] &&
|
|
(flag !== 'peer' && edge.type === 'peer' || edge.type === 'prod'))
|
|
.map(edge => edge.to),
|
|
})
|
|
}
|
|
}
|
|
|
|
module.exports = calcDepFlags
|