|
| 1 | +import { AbortController } from 'node-abort-controller'; |
1 | 2 | import type * as webpack from 'webpack';
|
2 | 3 |
|
3 | 4 | import type { FilesChange } from '../files-change';
|
4 |
| -import { consumeFilesChange } from '../files-change'; |
| 5 | +import { aggregateFilesChanges, consumeFilesChange } from '../files-change'; |
5 | 6 | import { getInfrastructureLogger } from '../infrastructure-logger';
|
6 | 7 | import type { ForkTsCheckerWebpackPluginConfig } from '../plugin-config';
|
7 | 8 | import { getPluginHooks } from '../plugin-hooks';
|
@@ -59,49 +60,91 @@ function tapStartToRunWorkers(
|
59 | 60 | return;
|
60 | 61 | }
|
61 | 62 |
|
| 63 | + // get current iteration number |
62 | 64 | const iteration = ++state.iteration;
|
63 | 65 |
|
64 |
| - let change: FilesChange = {}; |
| 66 | + // abort previous iteration |
| 67 | + if (state.abortController) { |
| 68 | + debug(`Aborting iteration ${iteration - 1}.`); |
| 69 | + state.abortController.abort(); |
| 70 | + } |
| 71 | + |
| 72 | + // create new abort controller for the new iteration |
| 73 | + const abortController = new AbortController(); |
| 74 | + state.abortController = abortController; |
| 75 | + |
| 76 | + let filesChange: FilesChange = {}; |
65 | 77 |
|
66 | 78 | if (state.watching) {
|
67 |
| - change = consumeFilesChange(compiler); |
| 79 | + filesChange = consumeFilesChange(compiler); |
68 | 80 | log(
|
69 | 81 | [
|
70 | 82 | 'Calling reporter service for incremental check.',
|
71 |
| - ` Changed files: ${JSON.stringify(change.changedFiles)}`, |
72 |
| - ` Deleted files: ${JSON.stringify(change.deletedFiles)}`, |
| 83 | + ` Changed files: ${JSON.stringify(filesChange.changedFiles)}`, |
| 84 | + ` Deleted files: ${JSON.stringify(filesChange.deletedFiles)}`, |
73 | 85 | ].join('\n')
|
74 | 86 | );
|
75 | 87 | } else {
|
76 | 88 | log('Calling reporter service for single check.');
|
77 | 89 | }
|
78 | 90 |
|
79 |
| - change = await hooks.start.promise(change, compilation); |
| 91 | + filesChange = await hooks.start.promise(filesChange, compilation); |
| 92 | + let aggregatedFilesChange = filesChange; |
| 93 | + if (state.aggregatedFilesChange) { |
| 94 | + aggregatedFilesChange = aggregateFilesChanges([aggregatedFilesChange, filesChange]); |
| 95 | + debug( |
| 96 | + [ |
| 97 | + `Aggregating with previous files change, iteration ${iteration}.`, |
| 98 | + ` Changed files: ${JSON.stringify(aggregatedFilesChange.changedFiles)}`, |
| 99 | + ` Deleted files: ${JSON.stringify(aggregatedFilesChange.deletedFiles)}`, |
| 100 | + ].join('\n') |
| 101 | + ); |
| 102 | + } |
| 103 | + state.aggregatedFilesChange = aggregatedFilesChange; |
| 104 | + |
| 105 | + // submit one at a time for a single compiler |
| 106 | + state.issuesPromise = (state.issuesPromise || Promise.resolve()) |
| 107 | + // resolve to undefined on error |
| 108 | + .catch(() => undefined) |
| 109 | + .then(() => { |
| 110 | + // early return |
| 111 | + if (abortController.signal.aborted) { |
| 112 | + return undefined; |
| 113 | + } |
| 114 | + |
| 115 | + debug(`Submitting the getIssuesWorker to the pool, iteration ${iteration}.`); |
| 116 | + return issuesPool.submit(async () => { |
| 117 | + try { |
| 118 | + debug(`Running the getIssuesWorker, iteration ${iteration}.`); |
| 119 | + const issues = await getIssuesWorker(aggregatedFilesChange, state.watching); |
| 120 | + if (state.aggregatedFilesChange === aggregatedFilesChange) { |
| 121 | + state.aggregatedFilesChange = undefined; |
| 122 | + } |
| 123 | + if (state.abortController === abortController) { |
| 124 | + state.abortController = undefined; |
| 125 | + } |
| 126 | + return issues; |
| 127 | + } catch (error) { |
| 128 | + hooks.error.call(error, compilation); |
| 129 | + return undefined; |
| 130 | + } finally { |
| 131 | + debug(`The getIssuesWorker finished its job, iteration ${iteration}.`); |
| 132 | + } |
| 133 | + }, abortController.signal); |
| 134 | + }); |
80 | 135 |
|
81 |
| - debug(`Submitting the getIssuesWorker to the pool, iteration ${iteration}.`); |
82 |
| - state.issuesPromise = issuesPool.submit(async () => { |
83 |
| - try { |
84 |
| - debug(`Running the getIssuesWorker, iteration ${iteration}.`); |
85 |
| - return await getIssuesWorker(change, state.watching); |
86 |
| - } catch (error) { |
87 |
| - hooks.error.call(error, compilation); |
88 |
| - return undefined; |
89 |
| - } finally { |
90 |
| - debug(`The getIssuesWorker finished its job, iteration ${iteration}.`); |
91 |
| - } |
92 |
| - }); |
93 | 136 | debug(`Submitting the getDependenciesWorker to the pool, iteration ${iteration}.`);
|
94 | 137 | state.dependenciesPromise = dependenciesPool.submit(async () => {
|
95 | 138 | try {
|
96 | 139 | debug(`Running the getDependenciesWorker, iteration ${iteration}.`);
|
97 |
| - return await getDependenciesWorker(change); |
| 140 | + return await getDependenciesWorker(filesChange); |
98 | 141 | } catch (error) {
|
99 | 142 | hooks.error.call(error, compilation);
|
100 | 143 | return undefined;
|
101 | 144 | } finally {
|
102 | 145 | debug(`The getDependenciesWorker finished its job, iteration ${iteration}.`);
|
103 | 146 | }
|
104 |
| - }); |
| 147 | + }); // don't pass abortController.signal because getDependencies() is blocking |
105 | 148 | });
|
106 | 149 | }
|
107 | 150 |
|
|
0 commit comments