Skip to content

Commit 4cb7e39

Browse files
authored
feat: use separate workers for dependencies and issues (#636)
In order to not block the next iteration on the getDependencies call, we use a separate worker for dependencies calculations (so getIssues from the previous compilation will not block the next getDependencies call) ✅ Closes: #612, #634
1 parent d660ad1 commit 4cb7e39

5 files changed

+85
-32
lines changed

src/ForkTsCheckerWebpackPlugin.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,18 @@ class ForkTsCheckerWebpackPlugin implements webpack.Plugin {
2929
*/
3030
static readonly version: string = '{{VERSION}}'; // will be replaced by the @semantic-release/exec
3131
/**
32-
* Default pool for the plugin concurrency limit
32+
* Default pools for the plugin concurrency limit
3333
*/
34-
static readonly pool: Pool = createPool(Math.max(1, os.cpus().length));
34+
static readonly issuesPool: Pool = createPool(Math.max(1, os.cpus().length));
35+
static readonly dependenciesPool: Pool = createPool(Math.max(1, os.cpus().length));
36+
37+
/**
38+
* @deprecated Use ForkTsCheckerWebpackPlugin.issuesPool instead
39+
*/
40+
static get pool(): Pool {
41+
// for backward compatibility
42+
return ForkTsCheckerWebpackPlugin.issuesPool;
43+
}
3544

3645
private readonly options: ForkTsCheckerWebpackPluginOptions;
3746

@@ -56,25 +65,37 @@ class ForkTsCheckerWebpackPlugin implements webpack.Plugin {
5665
apply(compiler: webpack.Compiler) {
5766
const configuration = createForkTsCheckerWebpackPluginConfiguration(compiler, this.options);
5867
const state = createForkTsCheckerWebpackPluginState();
59-
const reporters: ReporterRpcClient[] = [];
68+
const issuesReporters: ReporterRpcClient[] = [];
69+
const dependenciesReporters: ReporterRpcClient[] = [];
6070

6171
if (configuration.typescript.enabled) {
6272
assertTypeScriptSupport(configuration.typescript);
63-
reporters.push(createTypeScriptReporterRpcClient(configuration.typescript));
73+
issuesReporters.push(createTypeScriptReporterRpcClient(configuration.typescript));
74+
dependenciesReporters.push(createTypeScriptReporterRpcClient(configuration.typescript));
6475
}
6576

6677
if (configuration.eslint.enabled) {
6778
assertEsLintSupport(configuration.eslint);
68-
reporters.push(createEsLintReporterRpcClient(configuration.eslint));
79+
issuesReporters.push(createEsLintReporterRpcClient(configuration.eslint));
80+
dependenciesReporters.push(createEsLintReporterRpcClient(configuration.eslint));
6981
}
7082

71-
if (reporters.length) {
72-
const reporter = createAggregatedReporter(composeReporterRpcClients(reporters));
83+
if (issuesReporters.length) {
84+
const issuesReporter = createAggregatedReporter(composeReporterRpcClients(issuesReporters));
85+
const dependenciesReporter = createAggregatedReporter(
86+
composeReporterRpcClients(dependenciesReporters)
87+
);
7388

7489
tapAfterEnvironmentToPatchWatching(compiler, state);
75-
tapStartToConnectAndRunReporter(compiler, reporter, configuration, state);
90+
tapStartToConnectAndRunReporter(
91+
compiler,
92+
issuesReporter,
93+
dependenciesReporter,
94+
configuration,
95+
state
96+
);
7697
tapAfterCompileToAddDependencies(compiler, configuration, state);
77-
tapStopToDisconnectReporter(compiler, reporter, state);
98+
tapStopToDisconnectReporter(compiler, issuesReporter, dependenciesReporter, state);
7899
tapErrorToLogMessage(compiler, configuration);
79100
} else {
80101
throw new Error(

src/ForkTsCheckerWebpackPluginState.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { FilesMatch, Report } from './reporter';
33
import { Issue } from './issue';
44

55
interface ForkTsCheckerWebpackPluginState {
6-
reportPromise: Promise<Report | undefined>;
6+
issuesReportPromise: Promise<Report | undefined>;
7+
dependenciesReportPromise: Promise<Report | undefined>;
78
issuesPromise: Promise<Issue[] | undefined>;
89
dependenciesPromise: Promise<FilesMatch | undefined>;
910
lastDependencies: FilesMatch | undefined;
@@ -14,7 +15,8 @@ interface ForkTsCheckerWebpackPluginState {
1415

1516
function createForkTsCheckerWebpackPluginState(): ForkTsCheckerWebpackPluginState {
1617
return {
17-
reportPromise: Promise.resolve(undefined),
18+
issuesReportPromise: Promise.resolve(undefined),
19+
dependenciesReportPromise: Promise.resolve(undefined),
1820
issuesPromise: Promise.resolve(undefined),
1921
dependenciesPromise: Promise.resolve(undefined),
2022
lastDependencies: undefined,

src/hooks/tapDoneToAsyncGetIssues.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function tapDoneToAsyncGetIssues(
2222
return;
2323
}
2424

25-
const reportPromise = state.reportPromise;
25+
const reportPromise = state.issuesReportPromise;
2626
const issuesPromise = state.issuesPromise;
2727
let issues: Issue[] | undefined;
2828

@@ -46,7 +46,7 @@ function tapDoneToAsyncGetIssues(
4646
return;
4747
}
4848

49-
if (reportPromise !== state.reportPromise) {
49+
if (reportPromise !== state.issuesReportPromise) {
5050
// there is a newer report - ignore this one
5151
return;
5252
}

src/hooks/tapStartToConnectAndRunReporter.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { ForkTsCheckerWebpackPlugin } from '../ForkTsCheckerWebpackPlugin';
1212

1313
function tapStartToConnectAndRunReporter(
1414
compiler: webpack.Compiler,
15-
reporter: ReporterRpcClient,
15+
issuesReporter: ReporterRpcClient,
16+
dependenciesReporter: ReporterRpcClient,
1617
configuration: ForkTsCheckerWebpackPluginConfiguration,
1718
state: ForkTsCheckerWebpackPluginState
1819
) {
@@ -64,43 +65,70 @@ function tapStartToConnectAndRunReporter(
6465
}
6566

6667
let resolveDependencies: (dependencies: FilesMatch | undefined) => void;
67-
let rejectedDependencies: (error: Error) => void;
68+
let rejectDependencies: (error: Error) => void;
6869
let resolveIssues: (issues: Issue[] | undefined) => void;
6970
let rejectIssues: (error: Error) => void;
7071

7172
state.dependenciesPromise = new Promise((resolve, reject) => {
7273
resolveDependencies = resolve;
73-
rejectedDependencies = reject;
74+
rejectDependencies = reject;
7475
});
7576
state.issuesPromise = new Promise((resolve, reject) => {
7677
resolveIssues = resolve;
7778
rejectIssues = reject;
7879
});
79-
const previousReportPromise = state.reportPromise;
80-
state.reportPromise = ForkTsCheckerWebpackPlugin.pool.submit(
80+
const previousIssuesReportPromise = state.issuesReportPromise;
81+
const previousDependenciesReportPromise = state.dependenciesReportPromise;
82+
83+
change = await hooks.start.promise(change, compilation);
84+
85+
state.issuesReportPromise = ForkTsCheckerWebpackPlugin.issuesPool.submit(
8186
(done) =>
8287
new Promise(async (resolve) => {
83-
change = await hooks.start.promise(change, compilation);
88+
try {
89+
await issuesReporter.connect();
90+
91+
const previousReport = await previousIssuesReportPromise;
92+
if (previousReport) {
93+
await previousReport.close();
94+
}
8495

96+
const report = await issuesReporter.getReport(change);
97+
resolve(report);
98+
99+
report.getIssues().then(resolveIssues).catch(rejectIssues).finally(done);
100+
} catch (error) {
101+
if (error instanceof OperationCanceledError) {
102+
hooks.canceled.call(compilation);
103+
} else {
104+
hooks.error.call(error, compilation);
105+
}
106+
107+
resolve(undefined);
108+
resolveIssues(undefined);
109+
done();
110+
}
111+
})
112+
);
113+
state.dependenciesReportPromise = ForkTsCheckerWebpackPlugin.dependenciesPool.submit(
114+
(done) =>
115+
new Promise(async (resolve) => {
85116
try {
86-
await reporter.connect();
117+
await dependenciesReporter.connect();
87118

88-
const previousReport = await previousReportPromise;
119+
const previousReport = await previousDependenciesReportPromise;
89120
if (previousReport) {
90121
await previousReport.close();
91122
}
92123

93-
const report = await reporter.getReport(change);
124+
const report = await dependenciesReporter.getReport(change);
94125
resolve(report);
95126

96127
report
97128
.getDependencies()
98129
.then(resolveDependencies)
99-
.catch(rejectedDependencies)
100-
.finally(() => {
101-
// get issues after dependencies are resolved as it can be blocking
102-
report.getIssues().then(resolveIssues).catch(rejectIssues).finally(done);
103-
});
130+
.catch(rejectDependencies)
131+
.finally(done);
104132
} catch (error) {
105133
if (error instanceof OperationCanceledError) {
106134
hooks.canceled.call(compilation);
@@ -110,7 +138,6 @@ function tapStartToConnectAndRunReporter(
110138

111139
resolve(undefined);
112140
resolveDependencies(undefined);
113-
resolveIssues(undefined);
114141
done();
115142
}
116143
})

src/hooks/tapStopToDisconnectReporter.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,25 @@ import { ReporterRpcClient } from '../reporter';
44

55
function tapStopToDisconnectReporter(
66
compiler: webpack.Compiler,
7-
reporter: ReporterRpcClient,
7+
issuesReporter: ReporterRpcClient,
8+
dependenciesReporter: ReporterRpcClient,
89
state: ForkTsCheckerWebpackPluginState
910
) {
1011
compiler.hooks.watchClose.tap('ForkTsCheckerWebpackPlugin', () => {
11-
reporter.disconnect();
12+
issuesReporter.disconnect();
13+
dependenciesReporter.disconnect();
1214
});
1315

1416
compiler.hooks.done.tap('ForkTsCheckerWebpackPlugin', async () => {
1517
if (!state.watching) {
16-
await reporter.disconnect();
18+
await Promise.all([issuesReporter.disconnect(), dependenciesReporter.disconnect()]);
1719
}
1820
});
1921

2022
compiler.hooks.failed.tap('ForkTsCheckerWebpackPlugin', () => {
2123
if (!state.watching) {
22-
reporter.disconnect();
24+
issuesReporter.disconnect();
25+
dependenciesReporter.disconnect();
2326
}
2427
});
2528
}

0 commit comments

Comments
 (0)