node-module-collector
Version:
collect all production node modules used in a project
1,024 lines (1,014 loc) • 38.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
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/index.ts
var src_exports = {};
__export(src_exports, {
detect: () => detect,
getNodeModules: () => getNodeModules,
getNpmVersion: () => getNpmVersion
});
module.exports = __toCommonJS(src_exports);
// src/npmNodeModulesCollector.ts
var import_child_process = require("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
var import_path = __toESM(require("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, import_path.default.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(import_path.default.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 = (0, import_child_process.execSync)(this.getPMCommand(), {
cwd: this.rootDir,
encoding: "utf-8"
});
const dependencyTree = JSON.parse(npmListOutput);
return dependencyTree;
}
};
// src/pnpmNodeModulesCollector.ts
var import_child_process2 = require("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 = (0, import_child_process2.execSync)(this.getPMCommand(), {
cwd: this.rootDir,
encoding: "utf-8"
});
const dependencyTree = JSON.parse(pnpmListOutput)[0];
return dependencyTree;
}
};
// src/yarnNodeModulesCollector.ts
var import_child_process3 = require("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 = (0, import_child_process3.execSync)(this.getPMCommand(), {
cwd: this.rootDir,
encoding: "utf-8"
});
const dependencyTree = JSON.parse(npmListOutput);
return dependencyTree;
}
};
// src/packageManager.ts
var import_fs = require("fs");
var import_path2 = require("path");
var import_child_process4 = require("child_process");
var import_util = require("util");
var execa = (0, import_util.promisify)(import_child_process4.exec);
function pathExists(p) {
return __async(this, null, function* () {
try {
yield import_fs.promises.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 = (0, import_util.promisify)(import_child_process4.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((0, import_path2.resolve)(cwd, "yarn.lock")),
pathExists((0, import_path2.resolve)(cwd, "package-lock.json")),
pathExists((0, import_path2.resolve)(cwd, "pnpm-lock.yaml")),
pathExists((0, import_path2.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 = (0, import_path2.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();
}
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
detect,
getNodeModules,
getNpmVersion
});