UNPKG

node-module-collector

Version:

collect all production node modules used in a project

987 lines (978 loc) 36.6 kB
var __async = (__this, __arguments, generator) => { return new Promise((resolve2, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve2(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/npmNodeModulesCollector.ts import { execSync } from "child_process"; // src/hoist.ts var makeLocator = (name, reference) => `${name}@${reference}`; var makeIdent = (name, reference) => { const hashIdx = reference.indexOf(`#`); const realReference = hashIdx >= 0 ? reference.substring(hashIdx + 1) : reference; return makeLocator(name, realReference); }; var hoist = (tree, opts = {}) => { const debugLevel = opts.debugLevel || Number(process.env.NM_DEBUG_LEVEL || -1 /* NONE */); const check = opts.check || debugLevel >= 9 /* INTENSIVE_CHECK */; const hoistingLimits = opts.hoistingLimits || /* @__PURE__ */ new Map(); const options = { check, debugLevel, hoistingLimits, fastLookupPossible: true }; let startTime; if (options.debugLevel >= 0 /* PERF */) startTime = Date.now(); const treeCopy = cloneTree(tree, options); let anotherRoundNeeded = false; let round = 0; do { const result = hoistTo(treeCopy, [treeCopy], /* @__PURE__ */ new Set([treeCopy.locator]), /* @__PURE__ */ new Map(), options); anotherRoundNeeded = result.anotherRoundNeeded || result.isGraphChanged; options.fastLookupPossible = false; round++; } while (anotherRoundNeeded); if (options.debugLevel >= 0 /* PERF */) console.log(`hoist time: ${Date.now() - startTime}ms, rounds: ${round}`); if (options.debugLevel >= 1 /* CHECK */) { const prevTreeDump = dumpDepTree(treeCopy); const isGraphChanged = hoistTo(treeCopy, [treeCopy], /* @__PURE__ */ new Set([treeCopy.locator]), /* @__PURE__ */ new Map(), options).isGraphChanged; if (isGraphChanged) throw new Error(`The hoisting result is not terminal, prev tree: ${prevTreeDump}, next tree: ${dumpDepTree(treeCopy)}`); const checkLog = selfCheck(treeCopy); if (checkLog) { throw new Error(`${checkLog}, after hoisting finished: ${dumpDepTree(treeCopy)}`); } } if (options.debugLevel >= 2 /* REASONS */) console.log(dumpDepTree(treeCopy)); return shrinkTree(treeCopy); }; var getZeroRoundUsedDependencies = (rootNodePath) => { const rootNode = rootNodePath[rootNodePath.length - 1]; const usedDependencies = /* @__PURE__ */ new Map(); const seenNodes = /* @__PURE__ */ new Set(); const addUsedDependencies = (node) => { if (seenNodes.has(node)) return; seenNodes.add(node); for (const dep of node.hoistedDependencies.values()) usedDependencies.set(dep.name, dep); for (const dep of node.dependencies.values()) { if (!node.peerNames.has(dep.name)) { addUsedDependencies(dep); } } }; addUsedDependencies(rootNode); return usedDependencies; }; var getUsedDependencies = (rootNodePath) => { const rootNode = rootNodePath[rootNodePath.length - 1]; const usedDependencies = /* @__PURE__ */ new Map(); const seenNodes = /* @__PURE__ */ new Set(); const hiddenDependencies = /* @__PURE__ */ new Set(); const addUsedDependencies = (node, hiddenDependencies2) => { if (seenNodes.has(node)) return; seenNodes.add(node); for (const dep of node.hoistedDependencies.values()) { if (!hiddenDependencies2.has(dep.name)) { let reachableDependency; for (const node2 of rootNodePath) { reachableDependency = node2.dependencies.get(dep.name); if (reachableDependency) { usedDependencies.set(reachableDependency.name, reachableDependency); } } } } const childrenHiddenDependencies = /* @__PURE__ */ new Set(); for (const dep of node.dependencies.values()) childrenHiddenDependencies.add(dep.name); for (const dep of node.dependencies.values()) { if (!node.peerNames.has(dep.name)) { addUsedDependencies(dep, childrenHiddenDependencies); } } }; addUsedDependencies(rootNode, hiddenDependencies); return usedDependencies; }; var decoupleGraphNode = (parent, node) => { if (node.decoupled) return node; const { name, references, ident, locator, dependencies, originalDependencies, hoistedDependencies, peerNames, reasons, isHoistBorder, hoistPriority, dependencyKind, hoistedFrom, hoistedTo } = node; const clone = { name, references: new Set(references), ident, locator, dependencies: new Map(dependencies), originalDependencies: new Map(originalDependencies), hoistedDependencies: new Map(hoistedDependencies), peerNames: new Set(peerNames), reasons: new Map(reasons), decoupled: true, isHoistBorder, hoistPriority, dependencyKind, hoistedFrom: new Map(hoistedFrom), hoistedTo: new Map(hoistedTo) }; const selfDep = clone.dependencies.get(name); if (selfDep && selfDep.ident == clone.ident) clone.dependencies.set(name, clone); parent.dependencies.set(clone.name, clone); return clone; }; var getHoistIdentMap = (rootNode, preferenceMap) => { const identMap = /* @__PURE__ */ new Map([[rootNode.name, [rootNode.ident]]]); for (const dep of rootNode.dependencies.values()) { if (!rootNode.peerNames.has(dep.name)) { identMap.set(dep.name, [dep.ident]); } } const keyList = Array.from(preferenceMap.keys()); keyList.sort((key1, key2) => { const entry1 = preferenceMap.get(key1); const entry2 = preferenceMap.get(key2); if (entry2.hoistPriority !== entry1.hoistPriority) { return entry2.hoistPriority - entry1.hoistPriority; } else { const entry1Usages = entry1.dependents.size + entry1.peerDependents.size; const entry2Usages = entry2.dependents.size + entry2.peerDependents.size; return entry2Usages - entry1Usages; } }); for (const key of keyList) { const name = key.substring(0, key.indexOf(`@`, 1)); const ident = key.substring(name.length + 1); if (!rootNode.peerNames.has(name)) { let idents = identMap.get(name); if (!idents) { idents = []; identMap.set(name, idents); } if (idents.indexOf(ident) < 0) { idents.push(ident); } } } return identMap; }; var getSortedRegularDependencies = (node) => { const dependencies = /* @__PURE__ */ new Set(); const addDep = (dep, seenDeps = /* @__PURE__ */ new Set()) => { if (seenDeps.has(dep)) return; seenDeps.add(dep); for (const peerName of dep.peerNames) { if (!node.peerNames.has(peerName)) { const peerDep = node.dependencies.get(peerName); if (peerDep && !dependencies.has(peerDep)) { addDep(peerDep, seenDeps); } } } dependencies.add(dep); }; for (const dep of node.dependencies.values()) { if (!node.peerNames.has(dep.name)) { addDep(dep); } } return dependencies; }; var hoistTo = (tree, rootNodePath, rootNodePathLocators, parentShadowedNodes, options, seenNodes = /* @__PURE__ */ new Set()) => { const rootNode = rootNodePath[rootNodePath.length - 1]; if (seenNodes.has(rootNode)) return { anotherRoundNeeded: false, isGraphChanged: false }; seenNodes.add(rootNode); const preferenceMap = buildPreferenceMap(rootNode); const hoistIdentMap = getHoistIdentMap(rootNode, preferenceMap); const usedDependencies = tree == rootNode ? /* @__PURE__ */ new Map() : options.fastLookupPossible ? getZeroRoundUsedDependencies(rootNodePath) : getUsedDependencies(rootNodePath); let wasStateChanged; let anotherRoundNeeded = false; let isGraphChanged = false; const hoistIdents = new Map(Array.from(hoistIdentMap.entries()).map(([k, v]) => [k, v[0]])); const shadowedNodes = /* @__PURE__ */ new Map(); do { const result = hoistGraph(tree, rootNodePath, rootNodePathLocators, usedDependencies, hoistIdents, hoistIdentMap, parentShadowedNodes, shadowedNodes, options); if (result.isGraphChanged) isGraphChanged = true; if (result.anotherRoundNeeded) anotherRoundNeeded = true; wasStateChanged = false; for (const [name, idents] of hoistIdentMap) { if (idents.length > 1 && !rootNode.dependencies.has(name)) { hoistIdents.delete(name); idents.shift(); hoistIdents.set(name, idents[0]); wasStateChanged = true; } } } while (wasStateChanged); for (const dependency of rootNode.dependencies.values()) { if (!rootNode.peerNames.has(dependency.name) && !rootNodePathLocators.has(dependency.locator)) { rootNodePathLocators.add(dependency.locator); const result = hoistTo(tree, [...rootNodePath, dependency], rootNodePathLocators, shadowedNodes, options); if (result.isGraphChanged) isGraphChanged = true; if (result.anotherRoundNeeded) anotherRoundNeeded = true; rootNodePathLocators.delete(dependency.locator); } } return { anotherRoundNeeded, isGraphChanged }; }; var hasUnhoistedDependencies = (node) => { for (const [subName, subDependency] of node.dependencies) { if (!node.peerNames.has(subName) && subDependency.ident !== node.ident) { return true; } } return false; }; var getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason, fastLookupPossible }) => { let reasonRoot; let reason = null; let dependsOn = /* @__PURE__ */ new Set(); if (outputReason) reasonRoot = `${Array.from(rootNodePathLocators).map((x) => prettyPrintLocator(x)).join(`\u2192`)}`; const parentNode = nodePath[nodePath.length - 1]; const isSelfReference = node.ident === parentNode.ident; let isHoistable = !isSelfReference; if (outputReason && !isHoistable) reason = `- self-reference`; if (isHoistable) { isHoistable = node.dependencyKind !== 1 /* WORKSPACE */; if (outputReason && !isHoistable) { reason = `- workspace`; } } if (isHoistable && node.dependencyKind === 2 /* EXTERNAL_SOFT_LINK */) { isHoistable = !hasUnhoistedDependencies(node); if (outputReason && !isHoistable) { reason = `- external soft link with unhoisted dependencies`; } } if (isHoistable) { isHoistable = !rootNode.peerNames.has(node.name); if (outputReason && !isHoistable) { reason = `- cannot shadow peer: ${prettyPrintLocator(rootNode.originalDependencies.get(node.name).locator)} at ${reasonRoot}`; } } if (isHoistable) { let isNameAvailable = false; const usedDep = usedDependencies.get(node.name); isNameAvailable = !usedDep || usedDep.ident === node.ident; if (outputReason && !isNameAvailable) reason = `- filled by: ${prettyPrintLocator(usedDep.locator)} at ${reasonRoot}`; if (isNameAvailable) { for (let idx = nodePath.length - 1; idx >= 1; idx--) { const parent = nodePath[idx]; const parentDep = parent.dependencies.get(node.name); if (parentDep && parentDep.ident !== node.ident) { isNameAvailable = false; let shadowedNames = shadowedNodes.get(parentNode); if (!shadowedNames) { shadowedNames = /* @__PURE__ */ new Set(); shadowedNodes.set(parentNode, shadowedNames); } shadowedNames.add(node.name); if (outputReason) reason = `- filled by ${prettyPrintLocator(parentDep.locator)} at ${nodePath.slice(0, idx).map((x) => prettyPrintLocator(x.locator)).join(`\u2192`)}`; break; } } } isHoistable = isNameAvailable; } if (isHoistable) { const hoistedIdent = hoistIdents.get(node.name); isHoistable = hoistedIdent === node.ident; if (outputReason && !isHoistable) { reason = `- filled by: ${prettyPrintLocator(hoistIdentMap.get(node.name)[0])} at ${reasonRoot}`; } } if (isHoistable) { let arePeerDepsSatisfied = true; const checkList = new Set(node.peerNames); for (let idx = nodePath.length - 1; idx >= 1; idx--) { const parent = nodePath[idx]; for (const name of checkList) { if (parent.peerNames.has(name) && parent.originalDependencies.has(name)) continue; const parentDepNode = parent.dependencies.get(name); if (parentDepNode && rootNode.dependencies.get(name) !== parentDepNode) { if (idx === nodePath.length - 1) { dependsOn.add(parentDepNode); } else { dependsOn = null; arePeerDepsSatisfied = false; if (outputReason) { reason = `- peer dependency ${prettyPrintLocator(parentDepNode.locator)} from parent ${prettyPrintLocator(parent.locator)} was not hoisted to ${reasonRoot}`; } } } checkList.delete(name); } if (!arePeerDepsSatisfied) { break; } } isHoistable = arePeerDepsSatisfied; } if (isHoistable && !fastLookupPossible) { for (const origDep of node.hoistedDependencies.values()) { const usedDep = usedDependencies.get(origDep.name) || rootNode.dependencies.get(origDep.name); if (!usedDep || origDep.ident !== usedDep.ident) { isHoistable = false; if (outputReason) reason = `- previously hoisted dependency mismatch, needed: ${prettyPrintLocator(origDep.locator)}, available: ${prettyPrintLocator(usedDep == null ? void 0 : usedDep.locator)}`; break; } } } if (dependsOn !== null && dependsOn.size > 0) { return { isHoistable: 2 /* DEPENDS */, dependsOn, reason }; } else { return { isHoistable: isHoistable ? 0 /* YES */ : 1 /* NO */, reason }; } }; var getAliasedLocator = (node) => `${node.name}@${node.locator}`; var hoistGraph = (tree, rootNodePath, rootNodePathLocators, usedDependencies, hoistIdents, hoistIdentMap, parentShadowedNodes, shadowedNodes, options) => { const rootNode = rootNodePath[rootNodePath.length - 1]; const seenNodes = /* @__PURE__ */ new Set(); let anotherRoundNeeded = false; let isGraphChanged = false; const hoistNodeDependencies = (nodePath, locatorPath, aliasedLocatorPath, parentNode, newNodes2) => { if (seenNodes.has(parentNode)) return; const nextLocatorPath = [...locatorPath, getAliasedLocator(parentNode)]; const nextAliasedLocatorPath = [...aliasedLocatorPath, getAliasedLocator(parentNode)]; const dependantTree = /* @__PURE__ */ new Map(); const hoistInfos = /* @__PURE__ */ new Map(); for (const subDependency of getSortedRegularDependencies(parentNode)) { const hoistInfo = getNodeHoistInfo(rootNode, rootNodePathLocators, [rootNode, ...nodePath, parentNode], subDependency, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason: options.debugLevel >= 2 /* REASONS */, fastLookupPossible: options.fastLookupPossible }); hoistInfos.set(subDependency, hoistInfo); if (hoistInfo.isHoistable === 2 /* DEPENDS */) { for (const node of hoistInfo.dependsOn) { const nodeDependants = dependantTree.get(node.name) || /* @__PURE__ */ new Set(); nodeDependants.add(subDependency.name); dependantTree.set(node.name, nodeDependants); } } } const unhoistableNodes = /* @__PURE__ */ new Set(); const addUnhoistableNode = (node, hoistInfo, reason) => { if (!unhoistableNodes.has(node)) { unhoistableNodes.add(node); hoistInfos.set(node, { isHoistable: 1 /* NO */, reason }); for (const dependantName of dependantTree.get(node.name) || []) { addUnhoistableNode(parentNode.dependencies.get(dependantName), hoistInfo, options.debugLevel >= 2 /* REASONS */ ? `- peer dependency ${prettyPrintLocator(node.locator)} from parent ${prettyPrintLocator(parentNode.locator)} was not hoisted` : ``); } } }; for (const [node, hoistInfo] of hoistInfos) if (hoistInfo.isHoistable === 1 /* NO */) addUnhoistableNode(node, hoistInfo, hoistInfo.reason); let wereNodesHoisted = false; for (const node of hoistInfos.keys()) { if (!unhoistableNodes.has(node)) { isGraphChanged = true; const shadowedNames = parentShadowedNodes.get(parentNode); if (shadowedNames && shadowedNames.has(node.name)) anotherRoundNeeded = true; wereNodesHoisted = true; parentNode.dependencies.delete(node.name); parentNode.hoistedDependencies.set(node.name, node); parentNode.reasons.delete(node.name); const hoistedNode = rootNode.dependencies.get(node.name); if (options.debugLevel >= 2 /* REASONS */) { const hoistedFrom = Array.from(locatorPath).concat([parentNode.locator]).map((x) => prettyPrintLocator(x)).join(`\u2192`); let hoistedFromArray = rootNode.hoistedFrom.get(node.name); if (!hoistedFromArray) { hoistedFromArray = []; rootNode.hoistedFrom.set(node.name, hoistedFromArray); } hoistedFromArray.push(hoistedFrom); parentNode.hoistedTo.set(node.name, Array.from(rootNodePath).map((x) => prettyPrintLocator(x.locator)).join(`\u2192`)); } if (!hoistedNode) { if (rootNode.ident !== node.ident) { rootNode.dependencies.set(node.name, node); newNodes2.add(node); } } else { for (const reference of node.references) { hoistedNode.references.add(reference); } } } } if (parentNode.dependencyKind === 2 /* EXTERNAL_SOFT_LINK */ && wereNodesHoisted) anotherRoundNeeded = true; if (options.check) { const checkLog = selfCheck(tree); if (checkLog) { throw new Error(`${checkLog}, after hoisting dependencies of ${[rootNode, ...nodePath, parentNode].map((x) => prettyPrintLocator(x.locator)).join(`\u2192`)}: ${dumpDepTree(tree)}`); } } const children = getSortedRegularDependencies(parentNode); for (const node of children) { if (unhoistableNodes.has(node)) { const hoistInfo = hoistInfos.get(node); const hoistableIdent = hoistIdents.get(node.name); if ((hoistableIdent === node.ident || !parentNode.reasons.has(node.name)) && hoistInfo.isHoistable !== 0 /* YES */) parentNode.reasons.set(node.name, hoistInfo.reason); if (!node.isHoistBorder && nextAliasedLocatorPath.indexOf(getAliasedLocator(node)) < 0) { seenNodes.add(parentNode); const decoupledNode = decoupleGraphNode(parentNode, node); hoistNodeDependencies([...nodePath, parentNode], nextLocatorPath, nextAliasedLocatorPath, decoupledNode, nextNewNodes); seenNodes.delete(parentNode); } } } }; let newNodes; let nextNewNodes = new Set(getSortedRegularDependencies(rootNode)); const aliasedRootNodePathLocators = Array.from(rootNodePath).map((x) => getAliasedLocator(x)); do { newNodes = nextNewNodes; nextNewNodes = /* @__PURE__ */ new Set(); for (const dep of newNodes) { if (dep.locator === rootNode.locator || dep.isHoistBorder) continue; const decoupledDependency = decoupleGraphNode(rootNode, dep); hoistNodeDependencies([], Array.from(rootNodePathLocators), aliasedRootNodePathLocators, decoupledDependency, nextNewNodes); } } while (nextNewNodes.size > 0); return { anotherRoundNeeded, isGraphChanged }; }; var selfCheck = (tree) => { const log = []; const seenNodes = /* @__PURE__ */ new Set(); const parents = /* @__PURE__ */ new Set(); const checkNode = (node, parentDeps, parent) => { if (seenNodes.has(node)) return; seenNodes.add(node); if (parents.has(node)) return; const dependencies = new Map(parentDeps); for (const dep of node.dependencies.values()) if (!node.peerNames.has(dep.name)) dependencies.set(dep.name, dep); for (const origDep of node.originalDependencies.values()) { const dep = dependencies.get(origDep.name); const prettyPrintTreePath = () => `${Array.from(parents).concat([node]).map((x) => prettyPrintLocator(x.locator)).join(`\u2192`)}`; if (node.peerNames.has(origDep.name)) { const parentDep = parentDeps.get(origDep.name); if (parentDep !== dep || !parentDep || parentDep.ident !== origDep.ident) { log.push(`${prettyPrintTreePath()} - broken peer promise: expected ${origDep.ident} but found ${parentDep ? parentDep.ident : parentDep}`); } } else { const hoistedFrom = parent.hoistedFrom.get(node.name); const originalHoistedTo = node.hoistedTo.get(origDep.name); const prettyHoistedFrom = `${hoistedFrom ? ` hoisted from ${hoistedFrom.join(`, `)}` : ``}`; const prettyOriginalHoistedTo = `${originalHoistedTo ? ` hoisted to ${originalHoistedTo}` : ``}`; const prettyNodePath = `${prettyPrintTreePath()}${prettyHoistedFrom}`; if (!dep) { log.push(`${prettyNodePath} - broken require promise: no required dependency ${origDep.name}${prettyOriginalHoistedTo} found`); } else if (dep.ident !== origDep.ident) { log.push(`${prettyNodePath} - broken require promise for ${origDep.name}${prettyOriginalHoistedTo}: expected ${origDep.ident}, but found: ${dep.ident}`); } } } parents.add(node); for (const dep of node.dependencies.values()) { if (!node.peerNames.has(dep.name)) { checkNode(dep, dependencies, node); } } parents.delete(node); }; checkNode(tree, tree.dependencies, tree); return log.join(` `); }; var cloneTree = (tree, options) => { const { identName, name, reference, peerNames } = tree; const treeCopy = { name, references: /* @__PURE__ */ new Set([reference]), locator: makeLocator(identName, reference), ident: makeIdent(identName, reference), dependencies: /* @__PURE__ */ new Map(), originalDependencies: /* @__PURE__ */ new Map(), hoistedDependencies: /* @__PURE__ */ new Map(), peerNames: new Set(peerNames), reasons: /* @__PURE__ */ new Map(), decoupled: true, isHoistBorder: true, hoistPriority: 0, dependencyKind: 1 /* WORKSPACE */, hoistedFrom: /* @__PURE__ */ new Map(), hoistedTo: /* @__PURE__ */ new Map() }; const seenNodes = /* @__PURE__ */ new Map([[tree, treeCopy]]); const addNode = (node, parentNode) => { let workNode = seenNodes.get(node); const isSeen = !!workNode; if (!workNode) { const { name: name2, identName: identName2, reference: reference2, peerNames: peerNames2, hoistPriority, dependencyKind } = node; const dependenciesNmHoistingLimits = options.hoistingLimits.get(parentNode.locator); workNode = { name: name2, references: /* @__PURE__ */ new Set([reference2]), locator: makeLocator(identName2, reference2), ident: makeIdent(identName2, reference2), dependencies: /* @__PURE__ */ new Map(), originalDependencies: /* @__PURE__ */ new Map(), hoistedDependencies: /* @__PURE__ */ new Map(), peerNames: new Set(peerNames2), reasons: /* @__PURE__ */ new Map(), decoupled: true, isHoistBorder: dependenciesNmHoistingLimits ? dependenciesNmHoistingLimits.has(name2) : false, hoistPriority: hoistPriority || 0, dependencyKind: dependencyKind || 0 /* REGULAR */, hoistedFrom: /* @__PURE__ */ new Map(), hoistedTo: /* @__PURE__ */ new Map() }; seenNodes.set(node, workNode); } parentNode.dependencies.set(node.name, workNode); parentNode.originalDependencies.set(node.name, workNode); if (!isSeen) { for (const dep of node.dependencies) { addNode(dep, workNode); } } else { const seenCoupledNodes = /* @__PURE__ */ new Set(); const markNodeCoupled = (node2) => { if (seenCoupledNodes.has(node2)) return; seenCoupledNodes.add(node2); node2.decoupled = false; for (const dep of node2.dependencies.values()) { if (!node2.peerNames.has(dep.name)) { markNodeCoupled(dep); } } }; markNodeCoupled(workNode); } }; for (const dep of tree.dependencies) addNode(dep, treeCopy); return treeCopy; }; var getIdentName = (locator) => locator.substring(0, locator.indexOf(`@`, 1)); var shrinkTree = (tree) => { const treeCopy = { name: tree.name, identName: getIdentName(tree.locator), references: new Set(tree.references), dependencies: /* @__PURE__ */ new Set() }; const seenNodes = /* @__PURE__ */ new Set([tree]); const addNode = (node, parentWorkNode, parentNode) => { const isSeen = seenNodes.has(node); let resultNode; if (parentWorkNode === node) { resultNode = parentNode; } else { const { name, references, locator } = node; resultNode = { name, identName: getIdentName(locator), references, dependencies: /* @__PURE__ */ new Set() }; } parentNode.dependencies.add(resultNode); if (!isSeen) { seenNodes.add(node); for (const dep of node.dependencies.values()) { if (!node.peerNames.has(dep.name)) { addNode(dep, node, resultNode); } } seenNodes.delete(node); } }; for (const dep of tree.dependencies.values()) addNode(dep, tree, treeCopy); return treeCopy; }; var buildPreferenceMap = (rootNode) => { const preferenceMap = /* @__PURE__ */ new Map(); const seenNodes = /* @__PURE__ */ new Set([rootNode]); const getPreferenceKey = (node) => `${node.name}@${node.ident}`; const getOrCreatePreferenceEntry = (node) => { const key = getPreferenceKey(node); let entry = preferenceMap.get(key); if (!entry) { entry = { dependents: /* @__PURE__ */ new Set(), peerDependents: /* @__PURE__ */ new Set(), hoistPriority: 0 }; preferenceMap.set(key, entry); } return entry; }; const addDependent = (dependent, node) => { const isSeen = !!seenNodes.has(node); const entry = getOrCreatePreferenceEntry(node); entry.dependents.add(dependent.ident); if (!isSeen) { seenNodes.add(node); for (const dep of node.dependencies.values()) { const entry2 = getOrCreatePreferenceEntry(dep); entry2.hoistPriority = Math.max(entry2.hoistPriority, dep.hoistPriority); if (node.peerNames.has(dep.name)) { entry2.peerDependents.add(node.ident); } else { addDependent(node, dep); } } } }; for (const dep of rootNode.dependencies.values()) if (!rootNode.peerNames.has(dep.name)) addDependent(rootNode, dep); return preferenceMap; }; var prettyPrintLocator = (locator) => { if (!locator) return `none`; const idx = locator.indexOf(`@`, 1); let name = locator.substring(0, idx); if (name.endsWith(`$wsroot$`)) name = `wh:${name.replace(`$wsroot$`, ``)}`; const reference = locator.substring(idx + 1); if (reference === `workspace:.`) { return `.`; } else if (!reference) { return `${name}`; } else { let version = (reference.indexOf(`#`) > 0 ? reference.split(`#`)[1] : reference).replace(`npm:`, ``); if (reference.startsWith(`virtual`)) name = `v:${name}`; if (version.startsWith(`workspace`)) { name = `w:${name}`; version = ``; } return `${name}${version ? `@${version}` : ``}`; } }; var MAX_NODES_TO_DUMP = 5e4; var dumpDepTree = (tree) => { let nodeCount = 0; const dumpPackage = (pkg, parents, prefix = ``) => { if (nodeCount > MAX_NODES_TO_DUMP || parents.has(pkg)) return ``; nodeCount++; const dependencies = Array.from(pkg.dependencies.values()).sort((n1, n2) => { if (n1.name === n2.name) { return 0; } else { return n1.name > n2.name ? 1 : -1; } }); let str = ``; parents.add(pkg); for (let idx = 0; idx < dependencies.length; idx++) { const dep = dependencies[idx]; if (!pkg.peerNames.has(dep.name) && dep !== pkg) { const reason = pkg.reasons.get(dep.name); const identName = getIdentName(dep.locator); str += `${prefix}${idx < dependencies.length - 1 ? `\u251C\u2500` : `\u2514\u2500`}${(parents.has(dep) ? `>` : ``) + (identName !== dep.name ? `a:${dep.name}:` : ``) + prettyPrintLocator(dep.locator) + (reason ? ` ${reason}` : ``)} `; str += dumpPackage(dep, parents, `${prefix}${idx < dependencies.length - 1 ? `\u2502 ` : ` `}`); } } parents.delete(pkg); return str; }; const treeDump = dumpPackage(tree, /* @__PURE__ */ new Set()); return treeDump + (nodeCount > MAX_NODES_TO_DUMP ? ` Tree is too large, part of the tree has been dunped ` : ``); }; // src/nodeModulesCollector.ts import path from "path"; var NodeModulesCollector = class { constructor(rootDir) { this.dependencyPathMap = /* @__PURE__ */ new Map(); this.nodeModules = []; this.rootDir = rootDir; } transToHoisterTree(obj, key = `.`, nodes = /* @__PURE__ */ new Map()) { let node = nodes.get(key); const name = key.match(/@?[^@]+/)[0]; if (!node) { node = { name, identName: name, reference: key.match(/@?[^@]+@?(.+)?/)[1] || ``, dependencies: /* @__PURE__ */ new Set(), peerNames: /* @__PURE__ */ new Set([]) }; nodes.set(key, node); for (const dep of (obj[key] || {}).dependencies || []) { node.dependencies.add(this.transToHoisterTree(obj, dep, nodes)); } } return node; } TransToDependencyGraph(tree) { const result = { ".": {} }; const flatten = (node, parentKey = ".") => { var _a; const dependencies = node.dependencies || {}; for (const [key, value] of Object.entries(dependencies)) { if (Object.keys(value).length === 0) { continue; } const version = value.version || ""; const newKey = `${key}@${version}`; this.dependencyPathMap.set(newKey, path.normalize(value.path)); if (!((_a = result[parentKey]) == null ? void 0 : _a.dependencies)) { result[parentKey] = { dependencies: [] }; } result[parentKey].dependencies.push(newKey); flatten(value, newKey); } }; flatten(tree); return result; } _getNodeModules(dependencies, result) { if (dependencies.size === 0) return; for (let d of dependencies.values()) { const reference = [...d.references][0]; const p = this.dependencyPathMap.get(`${d.name}@${reference}`); let node = { name: d.name, version: reference, dir: p }; result.push(node); if (d.dependencies.size > 0) { node["dependencies"] = []; this._getNodeModules(d.dependencies, node["dependencies"]); } } result.sort((a, b) => a.name.localeCompare(b.name)); } getTreeFromWorkspaces(tree) { if (tree.workspaces && tree.dependencies) { for (const [key, value] of Object.entries(tree.dependencies)) { if (this.rootDir.endsWith(path.normalize(key))) { return value; } } } return tree; } getNodeModules() { const tree = this.getDependenciesTree(); const realTree = this.getTreeFromWorkspaces(tree); const dependencyGraph = this.TransToDependencyGraph(realTree); const hoisterResult = hoist(this.transToHoisterTree(dependencyGraph), { check: true }); this._getNodeModules(hoisterResult.dependencies, this.nodeModules); return this.nodeModules; } }; // src/npmNodeModulesCollector.ts var NpmNodeModulesCollector = class extends NodeModulesCollector { constructor(rootDir) { super(rootDir); } getPMCommand() { const cmd = process.platform === "win32" ? "npm.cmd" : "npm"; return `${cmd} list --omit dev -a --json --long`; } getDependenciesTree() { const npmListOutput = execSync(this.getPMCommand(), { cwd: this.rootDir, encoding: "utf-8" }); const dependencyTree = JSON.parse(npmListOutput); return dependencyTree; } }; // src/pnpmNodeModulesCollector.ts import { execSync as execSync2 } from "child_process"; var PnpmNodeModulesCollector = class extends NodeModulesCollector { constructor(rootDir) { super(rootDir); } getPMCommand() { const cmd = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; return `${cmd} list --prod --json --long --depth Infinity`; } getDependenciesTree() { const pnpmListOutput = execSync2(this.getPMCommand(), { cwd: this.rootDir, encoding: "utf-8" }); const dependencyTree = JSON.parse(pnpmListOutput)[0]; return dependencyTree; } }; // src/yarnNodeModulesCollector.ts import { execSync as execSync3 } from "child_process"; var YarnNodeModulesCollector = class extends NodeModulesCollector { constructor(rootDir) { super(rootDir); } getPMCommand() { const cmd = process.platform === "win32" ? "npm.cmd" : "npm"; return `${cmd} list --omit dev -a --json --long`; } getDependenciesTree() { const npmListOutput = execSync3(this.getPMCommand(), { cwd: this.rootDir, encoding: "utf-8" }); const dependencyTree = JSON.parse(npmListOutput); return dependencyTree; } }; // src/packageManager.ts import { promises as fs } from "fs"; import { resolve, dirname } from "path"; import { exec } from "child_process"; import { promisify } from "util"; var execa = promisify(exec); function pathExists(p) { return __async(this, null, function* () { try { yield fs.access(p); return true; } catch (e) { return false; } }); } var cache = /* @__PURE__ */ new Map(); function hasGlobalInstallation(pm) { const key = `has_global_${pm}`; if (cache.has(key)) { return Promise.resolve(cache.get(key)); } const execa2 = promisify(exec); return execa2(`${pm} --version`).then((res) => { return /^\d+.\d+.\d+$/.test(res.stdout); }).then((value) => { cache.set(key, value); return value; }).catch(() => false); } function getTypeofLockFile(cwd = ".") { const key = `lockfile_${cwd}`; if (cache.has(key)) { return Promise.resolve(cache.get(key)); } return Promise.all([ pathExists(resolve(cwd, "yarn.lock")), pathExists(resolve(cwd, "package-lock.json")), pathExists(resolve(cwd, "pnpm-lock.yaml")), pathExists(resolve(cwd, "bun.lockb")) ]).then(([isYarn, isNpm, isPnpm, isBun]) => { let value = null; if (isYarn) { value = "yarn"; } else if (isPnpm) { value = "pnpm"; } else if (isBun) { value = "bun"; } else if (isNpm) { value = "npm"; } cache.set(key, value); return value; }); } var detect = (..._0) => __async(void 0, [..._0], function* ({ cwd, includeGlobalBun } = {}) { let type = yield getTypeofLockFile(cwd); if (type) { return type; } let tmpCwd = cwd; for (let i = 1; i <= 5; i++) { tmpCwd = dirname(tmpCwd); type = yield getTypeofLockFile(tmpCwd); if (type) { return type; } } const [hasYarn, hasPnpm, hasBun] = yield Promise.all([ hasGlobalInstallation("yarn"), hasGlobalInstallation("pnpm"), includeGlobalBun && hasGlobalInstallation("bun") ]); if (hasYarn) { return "yarn"; } if (hasPnpm) { return "pnpm"; } if (hasBun) { return "bun"; } return "npm"; }); function getNpmVersion(pm) { return execa(`${pm} --version`).then((res) => res.stdout); } // src/index.ts function getCollectorByPackageManager(rootDir) { return __async(this, null, function* () { const manager = yield detect({ cwd: rootDir }); switch (manager) { case "npm": return new NpmNodeModulesCollector(rootDir); case "pnpm": return new PnpmNodeModulesCollector(rootDir); case "yarn": return new YarnNodeModulesCollector(rootDir); default: console.error(`Unsupported package manager: ${manager}`); return void 0; } }); } function getNodeModules(rootDir) { return __async(this, null, function* () { const collector = yield getCollectorByPackageManager(rootDir); if (collector) { return collector.getNodeModules(); } }); } export { detect, getNodeModules, getNpmVersion };