diff --git a/commands/run/index.js b/commands/run/index.js index d2178ee25b..810bdd77d4 100644 --- a/commands/run/index.js +++ b/commands/run/index.js @@ -1,13 +1,12 @@ "use strict"; const pMap = require("p-map"); -const PQueue = require("p-queue"); const Command = require("@lerna/command"); const npmRunScript = require("@lerna/npm-run-script"); const output = require("@lerna/output"); const timer = require("@lerna/timer"); -const QueryGraph = require("@lerna/query-graph"); +const runTopologically = require("@lerna/run-topologically"); const ValidationError = require("@lerna/validation-error"); const { getFilteredPackages } = require("@lerna/filter-options"); @@ -129,33 +128,15 @@ class RunCommand extends Command { } runScriptInPackagesTopological() { - const queue = new PQueue({ concurrency: this.concurrency }); - const graph = new QueryGraph(this.packagesWithScript, this.options.rejectCycles); - const runner = this.options.stream ? pkg => this.runScriptInPackageStreaming(pkg) : pkg => this.runScriptInPackageCapturing(pkg); - return new Promise((resolve, reject) => { - const returnValues = []; - - const queueNextAvailablePackages = () => - graph.getAvailablePackages().forEach(({ pkg, name }) => { - graph.markAsTaken(name); - - queue - .add(() => - runner(pkg) - .then(value => returnValues.push(value)) - .then(() => graph.markAsDone(pkg)) - .then(() => queueNextAvailablePackages()) - ) - .catch(reject); - }); - - queueNextAvailablePackages(); - - return queue.onIdle().then(() => resolve(returnValues)); + return runTopologically({ + packages: this.packagesWithScript, + concurrency: this.concurrency, + rejectCycles: this.options.rejectCycles, + runner, }); } diff --git a/commands/run/package.json b/commands/run/package.json index 732f721105..1e5891da18 100644 --- a/commands/run/package.json +++ b/commands/run/package.json @@ -39,10 +39,9 @@ "@lerna/filter-options": "file:../../core/filter-options", "@lerna/npm-run-script": "file:../../utils/npm-run-script", "@lerna/output": "file:../../utils/output", - "@lerna/query-graph": "file:../../utils/query-graph", + "@lerna/run-topologically": "file:../../utils/run-topologically", "@lerna/timer": "file:../../utils/timer", "@lerna/validation-error": "file:../../core/validation-error", - "p-map": "^1.2.0", - "p-queue": "^4.0.0" + "p-map": "^1.2.0" } } diff --git a/package-lock.json b/package-lock.json index c7c888e0a0..49708cbcf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1182,11 +1182,10 @@ "@lerna/filter-options": "file:core/filter-options", "@lerna/npm-run-script": "file:utils/npm-run-script", "@lerna/output": "file:utils/output", - "@lerna/query-graph": "file:utils/query-graph", + "@lerna/run-topologically": "file:utils/run-topologically", "@lerna/timer": "file:utils/timer", "@lerna/validation-error": "file:core/validation-error", - "p-map": "^1.2.0", - "p-queue": "^4.0.0" + "p-map": "^1.2.0" } }, "@lerna/run-lifecycle": { @@ -1205,6 +1204,14 @@ "p-map-series": "^1.0.0" } }, + "@lerna/run-topologically": { + "version": "file:utils/run-topologically", + "requires": { + "@lerna/query-graph": "file:utils/query-graph", + "figgy-pudding": "^3.5.1", + "p-queue": "^4.0.0" + } + }, "@lerna/symlink-binary": { "version": "file:utils/symlink-binary", "requires": { diff --git a/utils/run-topologically/README.md b/utils/run-topologically/README.md new file mode 100644 index 0000000000..c63efc543a --- /dev/null +++ b/utils/run-topologically/README.md @@ -0,0 +1,9 @@ +# `@lerna/run-topologically` + +> An internal Lerna tool + +## Usage + +You probably shouldn't, at least directly. + +Install [lerna](https://www.npmjs.com/package/lerna) for access to the `lerna` CLI. diff --git a/utils/run-topologically/package.json b/utils/run-topologically/package.json new file mode 100644 index 0000000000..2993b41b64 --- /dev/null +++ b/utils/run-topologically/package.json @@ -0,0 +1,38 @@ +{ + "name": "@lerna/run-topologically", + "version": "3.13.4", + "description": "An internal Lerna tool", + "keywords": [ + "lerna", + "utils" + ], + "homepage": "https://github.com/lerna/lerna/tree/master/utils/run-topologically#readme", + "license": "MIT", + "author": { + "name": "Daniel Stockman", + "url": "https://github.com/evocateur" + }, + "files": [ + "run-topologically.js" + ], + "main": "run-topologically.js", + "engines": { + "node": ">= 6.9.0" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lerna/lerna.git", + "directory": "utils/run-topologically" + }, + "scripts": { + "test": "echo \"Run tests from root\" && exit 1" + }, + "dependencies": { + "@lerna/query-graph": "file:../query-graph", + "figgy-pudding": "^3.5.1", + "p-queue": "^4.0.0" + } +} diff --git a/utils/run-topologically/run-topologically.js b/utils/run-topologically/run-topologically.js new file mode 100644 index 0000000000..354f87f613 --- /dev/null +++ b/utils/run-topologically/run-topologically.js @@ -0,0 +1,45 @@ +"use strict"; + +const PQueue = require("p-queue"); +const figgyPudding = require("figgy-pudding"); +const QueryGraph = require("@lerna/query-graph"); + +module.exports = runTopologically; + +const TopologicalConfig = figgyPudding({ + packages: {}, + concurrency: {}, + "reject-cycles": {}, + rejectCycles: "reject-cycles", + runner: {}, +}); + +function runTopologically(_opts) { + const opts = TopologicalConfig(_opts); + const { packages, concurrency, rejectCycles, runner } = opts; + + const queue = new PQueue({ concurrency }); + const graph = new QueryGraph(packages, rejectCycles); + + return new Promise((resolve, reject) => { + const returnValues = []; + + const queueNextAvailablePackages = () => + graph.getAvailablePackages().forEach(({ pkg, name }) => { + graph.markAsTaken(name); + + queue + .add(() => + runner(pkg) + .then(value => returnValues.push(value)) + .then(() => graph.markAsDone(pkg)) + .then(() => queueNextAvailablePackages()) + ) + .catch(reject); + }); + + queueNextAvailablePackages(); + + return queue.onIdle().then(() => resolve(returnValues)); + }); +}