123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- "use strict";
- const { PackageGraph } = require("@lerna/package-graph");
- /**
- * @typedef {object} QueryGraphConfig
- * @property {'allDependencies'|'dependencies'} [graphType] "dependencies" excludes devDependencies from graph
- * @property {boolean} [rejectCycles] Whether or not to reject dependency cycles
- */
- /**
- * A mutable PackageGraph used to query for next available packages.
- */
- class QueryGraph {
- /**
- * Sort a list of Packages topologically.
- *
- * @param {import("@lerna/package").Package[]} packages An array of Packages to build the list out of
- * @param {QueryGraphConfig} [options]
- *
- * @returns {import("@lerna/package").Package[]} A list of Package instances in topological order
- */
- static toposort(packages, options) {
- const graph = new QueryGraph(packages, options);
- const result = [];
- let batch = graph.getAvailablePackages();
- while (batch.length) {
- for (const node of batch) {
- // no need to take() in synchronous loop
- result.push(node.pkg);
- graph.markAsDone(node);
- }
- batch = graph.getAvailablePackages();
- }
- return result;
- }
- /**
- * @param {import("@lerna/package").Package[]} packages An array of Packages to build the graph out of
- * @param {QueryGraphConfig} [options]
- */
- constructor(packages, { graphType = "allDependencies", rejectCycles } = {}) {
- // Create dependency graph
- this.graph = new PackageGraph(packages, graphType);
- // Evaluate cycles
- this.cycles = this.graph.collapseCycles(rejectCycles);
- }
- _getNextLeaf() {
- return Array.from(this.graph.values()).filter((node) => node.localDependencies.size === 0);
- }
- _getNextCycle() {
- const cycle = Array.from(this.cycles).find((cycleNode) => cycleNode.localDependencies.size === 0);
- if (!cycle) {
- return [];
- }
- this.cycles.delete(cycle);
- return cycle.flatten();
- }
- getAvailablePackages() {
- // Get the next leaf nodes
- const availablePackages = this._getNextLeaf();
- if (availablePackages.length > 0) {
- return availablePackages;
- }
- return this._getNextCycle();
- }
- /**
- * @param {string} name
- */
- markAsTaken(name) {
- this.graph.delete(name);
- }
- /**
- * @param {import("@lerna/package-graph").PackageGraphNode} candidateNode
- */
- markAsDone(candidateNode) {
- this.graph.remove(candidateNode);
- for (const cycle of this.cycles) {
- cycle.unlink(candidateNode);
- }
- }
- }
- module.exports.QueryGraph = QueryGraph;
- module.exports.toposort = QueryGraph.toposort;
|