run-topologically.js 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. "use strict";
  2. const PQueue = require("p-queue").default;
  3. const { QueryGraph } = require("@lerna/query-graph");
  4. module.exports.runTopologically = runTopologically;
  5. /**
  6. * @typedef {import("@lerna/query-graph").QueryGraphConfig & { concurrency: number }} TopologicalConfig
  7. */
  8. /**
  9. * Run callback in maximally-saturated topological order.
  10. *
  11. * @template T
  12. * @param {import("@lerna/package").Package[]} packages List of `Package` instances
  13. * @param {(pkg: import("@lerna/package").Package) => Promise<T>} runner Callback to map each `Package` with
  14. * @param {TopologicalConfig} [options]
  15. * @returns {Promise<T[]>} when all executions complete
  16. */
  17. function runTopologically(packages, runner, { concurrency, graphType, rejectCycles } = {}) {
  18. const queue = new PQueue({ concurrency });
  19. const graph = new QueryGraph(packages, { graphType, rejectCycles });
  20. return new Promise((resolve, reject) => {
  21. const returnValues = [];
  22. const queueNextAvailablePackages = () =>
  23. graph.getAvailablePackages().forEach(({ pkg, name }) => {
  24. graph.markAsTaken(name);
  25. queue
  26. .add(() =>
  27. runner(pkg)
  28. .then((value) => returnValues.push(value))
  29. .then(() => graph.markAsDone(pkg))
  30. .then(() => queueNextAvailablePackages())
  31. )
  32. .catch(reject);
  33. });
  34. queueNextAvailablePackages();
  35. return queue.onIdle().then(() => resolve(returnValues));
  36. });
  37. }