* Returns result of setup. * @template SetupResult * @param {(compiler: Compiler, index: number, doneCallback: Callback<Stats>, isBlocked: () => boolean, setChanged: () => void, setInvalid: () => void) => SetupResult} setup setup a single compiler * @param {(compiler: Compiler, setupResult: Se
(setup, run, callback)
| 436 | * @returns {SetupResult[]} result of setup |
| 437 | */ |
| 438 | _runGraph(setup, run, callback) { |
| 439 | /** @typedef {{ compiler: Compiler, setupResult: undefined | SetupResult, result: undefined | Stats, state: "pending" | "blocked" | "queued" | "starting" | "running" | "running-outdated" | "done", children: Node[], parents: Node[] }} Node */ |
| 440 | |
| 441 | // State transitions for nodes: |
| 442 | // -> blocked (initial) |
| 443 | // blocked -> starting [running++] (when all parents done) |
| 444 | // queued -> starting [running++] (when processing the queue) |
| 445 | // starting -> running (when run has been called) |
| 446 | // running -> done [running--] (when compilation is done) |
| 447 | // done -> pending (when invalidated from file change) |
| 448 | // pending -> blocked [add to queue] (when invalidated from aggregated changes) |
| 449 | // done -> blocked [add to queue] (when invalidated, from parent invalidation) |
| 450 | // running -> running-outdated (when invalidated, either from change or parent invalidation) |
| 451 | // running-outdated -> blocked [running--] (when compilation is done) |
| 452 | |
| 453 | /** @type {Node[]} */ |
| 454 | const nodes = this.compilers.map((compiler) => ({ |
| 455 | compiler, |
| 456 | setupResult: undefined, |
| 457 | result: undefined, |
| 458 | state: "blocked", |
| 459 | children: [], |
| 460 | parents: [] |
| 461 | })); |
| 462 | /** @type {Map<string, Node>} */ |
| 463 | const compilerToNode = new Map(); |
| 464 | for (const node of nodes) { |
| 465 | compilerToNode.set(/** @type {string} */ (node.compiler.name), node); |
| 466 | } |
| 467 | for (const node of nodes) { |
| 468 | const dependencies = this.dependencies.get(node.compiler); |
| 469 | if (!dependencies) continue; |
| 470 | for (const dep of dependencies) { |
| 471 | const parent = /** @type {Node} */ (compilerToNode.get(dep)); |
| 472 | node.parents.push(parent); |
| 473 | parent.children.push(node); |
| 474 | } |
| 475 | } |
| 476 | /** @type {ArrayQueue<Node>} */ |
| 477 | const queue = new ArrayQueue(); |
| 478 | for (const node of nodes) { |
| 479 | if (node.parents.length === 0) { |
| 480 | node.state = "queued"; |
| 481 | queue.enqueue(node); |
| 482 | } |
| 483 | } |
| 484 | let errored = false; |
| 485 | let running = 0; |
| 486 | const parallelism = /** @type {number} */ (this._options.parallelism); |
| 487 | /** |
| 488 | * Processes the provided node. |
| 489 | * @param {Node} node node |
| 490 | * @param {(Error | null)=} err error |
| 491 | * @param {Stats=} stats result |
| 492 | * @returns {void} |
| 493 | */ |
| 494 | const nodeDone = (node, err, stats) => { |
| 495 | if (errored) return; |