123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- "use strict";
- const npa = require("npm-package-arg");
- const { ValidationError } = require("@lerna/validation-error");
- const { CyclicPackageGraphNode } = require("./lib/cyclic-package-graph-node");
- const { PackageGraphNode } = require("./lib/package-graph-node");
- const { reportCycles } = require("./lib/report-cycles");
- class PackageGraph extends Map {
-
- constructor(packages, graphType = "allDependencies", forceLocal) {
- super(packages.map((pkg) => [pkg.name, new PackageGraphNode(pkg)]));
- if (packages.length !== this.size) {
-
- const seen = new Map();
- for (const { name, location } of packages) {
- if (seen.has(name)) {
- seen.get(name).push(location);
- } else {
- seen.set(name, [location]);
- }
- }
- for (const [name, locations] of seen) {
- if (locations.length > 1) {
- throw new ValidationError(
- "ENAME",
- [`Package name "${name}" used in multiple packages:`, ...locations].join("\n\t")
- );
- }
- }
- }
- this.forEach((currentNode, currentName) => {
- const graphDependencies =
- graphType === "dependencies"
- ? Object.assign({}, currentNode.pkg.optionalDependencies, currentNode.pkg.dependencies)
- : Object.assign(
- {},
- currentNode.pkg.devDependencies,
- currentNode.pkg.optionalDependencies,
- currentNode.pkg.dependencies
- );
- Object.keys(graphDependencies).forEach((depName) => {
- const depNode = this.get(depName);
-
-
-
- const spec = graphDependencies[depName].replace(/^link:/, "file:");
- const resolved = npa.resolve(depName, spec, currentNode.location);
- if (!depNode) {
-
- return currentNode.externalDependencies.set(depName, resolved);
- }
- if (forceLocal || resolved.fetchSpec === depNode.location || depNode.satisfies(resolved)) {
-
- currentNode.localDependencies.set(depName, resolved);
- depNode.localDependents.set(currentName, currentNode);
- } else {
-
- currentNode.externalDependencies.set(depName, resolved);
- }
- });
- });
- }
- get rawPackageList() {
- return Array.from(this.values()).map((node) => node.pkg);
- }
-
- addDependencies(filteredPackages) {
- return this.extendList(filteredPackages, "localDependencies");
- }
-
- addDependents(filteredPackages) {
- return this.extendList(filteredPackages, "localDependents");
- }
-
- extendList(packageList, nodeProp) {
-
- const search = new Set(packageList.map(({ name }) => this.get(name)));
-
-
- const result = [];
- search.forEach((currentNode) => {
-
- result.push(currentNode);
- currentNode[nodeProp].forEach((meta, depName) => {
- const depNode = this.get(depName);
- if (depNode !== currentNode && !search.has(depNode)) {
- search.add(depNode);
- }
- });
- });
-
- return result.map((node) => node.pkg);
- }
-
- partitionCycles(rejectCycles) {
- const cyclePaths = new Set();
- const cycleNodes = new Set();
- this.forEach((currentNode, currentName) => {
- const seen = new Set();
- const visits = (walk) => (dependentNode, dependentName, siblingDependents) => {
- const step = walk.concat(dependentName);
- if (seen.has(dependentNode)) {
- return;
- }
- seen.add(dependentNode);
- if (dependentNode === currentNode) {
-
- cycleNodes.add(currentNode);
- cyclePaths.add(step);
- return;
- }
- if (siblingDependents.has(currentName)) {
-
- const cycleDependentName = Array.from(dependentNode.localDependencies.keys()).find((key) =>
- currentNode.localDependents.has(key)
- );
- const pathToCycle = step.slice().reverse().concat(cycleDependentName);
- cycleNodes.add(dependentNode);
- cyclePaths.add(pathToCycle);
- }
- dependentNode.localDependents.forEach(visits(step));
- };
- currentNode.localDependents.forEach(visits([currentName]));
- });
- reportCycles(
- Array.from(cyclePaths, (cycle) => cycle.join(" -> ")),
- rejectCycles
- );
- return [cyclePaths, cycleNodes];
- }
-
- collapseCycles(rejectCycles) {
-
- const cyclePaths = [];
-
- const nodeToCycle = new Map();
-
- const cycles = new Set();
-
- const walkStack = [];
- function visits(baseNode, dependentNode) {
- if (nodeToCycle.has(baseNode)) {
- return;
- }
- let topLevelDependent = dependentNode;
- while (nodeToCycle.has(topLevelDependent)) {
- topLevelDependent = nodeToCycle.get(topLevelDependent);
- }
- if (
- topLevelDependent === baseNode ||
- (topLevelDependent.isCycle && topLevelDependent.has(baseNode.name))
- ) {
- const cycle = new CyclicPackageGraphNode();
- walkStack.forEach((nodeInCycle) => {
- nodeToCycle.set(nodeInCycle, cycle);
- cycle.insert(nodeInCycle);
- cycles.delete(nodeInCycle);
- });
- cycles.add(cycle);
- cyclePaths.push(cycle.toString());
- return;
- }
- if (walkStack.indexOf(topLevelDependent) === -1) {
-
- visitWithStack(baseNode, topLevelDependent);
- }
- }
- function visitWithStack(baseNode, currentNode = baseNode) {
- walkStack.push(currentNode);
- currentNode.localDependents.forEach(visits.bind(null, baseNode));
- walkStack.pop();
- }
- this.forEach((currentNode) => visitWithStack(currentNode));
- cycles.forEach((collapsedNode) => visitWithStack(collapsedNode));
- reportCycles(cyclePaths, rejectCycles);
- return cycles;
- }
-
- pruneCycleNodes(cycleNodes) {
- return this.prune(...cycleNodes);
- }
-
- prune(...candidates) {
- if (candidates.length === this.size) {
- return this.clear();
- }
- candidates.forEach((node) => this.remove(node));
- }
-
- remove(candidateNode) {
- this.delete(candidateNode.name);
- this.forEach((node) => {
-
- node.localDependencies.delete(candidateNode.name);
-
- node.localDependents.delete(candidateNode.name);
- });
- }
- }
- module.exports.PackageGraph = PackageGraph;
|