Skip to content

Commit a195dd5

Browse files
authored
feat: use simple program for single compilation (no watch) (#574)
Use simple ts.Program and ts.CompilerHost when running webpack in non-watch mode. This can improve check time in CI for some cases. BREAKING CHANGE: 🧨 Use ts.Program and ts.CompilerHost for single compilation by default ✅ Closes: #572
1 parent 1057b23 commit a195dd5

File tree

8 files changed

+82
-21
lines changed

8 files changed

+82
-21
lines changed

src/hooks/tapStartToConnectAndRunReporter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ function tapStartToConnectAndRunReporter(
9090
await previousReport.close();
9191
}
9292

93-
const report = await reporter.getReport(change);
93+
const report = await reporter.getReport(change, state.watching);
9494
resolve(report);
9595

9696
report

src/reporter/AggregatedReporter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function createAggregatedReporter<TReporter extends Reporter>(reporter: TReporte
1212

1313
const aggregatedReporter: TReporter = {
1414
...reporter,
15-
getReport: async (change) => {
15+
getReport: async (change, watching) => {
1616
if (!pendingPromise) {
1717
let resolvePending: () => void;
1818
pendingPromise = new Promise((resolve) => {
@@ -23,7 +23,7 @@ function createAggregatedReporter<TReporter extends Reporter>(reporter: TReporte
2323
});
2424

2525
return reporter
26-
.getReport(change)
26+
.getReport(change, watching)
2727
.then((report) => ({
2828
...report,
2929
async close() {
@@ -45,7 +45,7 @@ function createAggregatedReporter<TReporter extends Reporter>(reporter: TReporte
4545
const change = aggregateFilesChanges(queuedChanges);
4646
queuedChanges = [];
4747

48-
return aggregatedReporter.getReport(change);
48+
return aggregatedReporter.getReport(change, watching);
4949
} else {
5050
throw new OperationCanceledError('getReport canceled - new report requested.');
5151
}

src/reporter/Reporter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FilesChange } from './FilesChange';
22
import { Report } from './Report';
33

44
interface Reporter {
5-
getReport(change: FilesChange): Promise<Report>;
5+
getReport(change: FilesChange, watching: boolean): Promise<Report>;
66
}
77

88
export { Reporter };

src/reporter/reporter-rpc/ReporterRpcClient.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ function createReporterRpcClient<TConfiguration extends object>(
4141
await channel.close();
4242
}
4343
},
44-
getReport: async (change) => {
45-
const reportId = await rpcClient.dispatchCall(getReport, change);
44+
getReport: async (change, watching) => {
45+
const reportId = await rpcClient.dispatchCall(getReport, { change, watching });
4646

4747
return {
4848
getDependencies() {
@@ -65,8 +65,8 @@ function composeReporterRpcClients(clients: ReporterRpcClient[]): ReporterRpcCli
6565
connect: () => Promise.all(clients.map((client) => client.connect())).then(() => undefined),
6666
disconnect: () =>
6767
Promise.all(clients.map((client) => client.disconnect())).then(() => undefined),
68-
getReport: (change: FilesChange) =>
69-
Promise.all(clients.map((client) => client.getReport(change))).then((reports) => ({
68+
getReport: (change: FilesChange, watching: boolean) =>
69+
Promise.all(clients.map((client) => client.getReport(change, watching))).then((reports) => ({
7070
getDependencies: () =>
7171
Promise.all(reports.map((report) => report.getDependencies())).then((dependencies) =>
7272
dependencies.reduce(

src/reporter/reporter-rpc/ReporterRpcProcedure.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Issue } from '../../issue';
44
import { Dependencies } from '../Dependencies';
55

66
const configure: RpcProcedure<object, void> = 'configure';
7-
const getReport: RpcProcedure<FilesChange, void> = 'getReport';
7+
const getReport: RpcProcedure<{ change: FilesChange; watching: boolean }, void> = 'getReport';
88
const getDependencies: RpcProcedure<void, Dependencies> = 'getDependencies';
99
const getIssues: RpcProcedure<void, Issue[]> = 'getIssues';
1010
const closeReport: RpcProcedure<void, void> = 'closeReport';

src/reporter/reporter-rpc/ReporterRpcService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ function registerReporterRpcService<TConfiguration extends object>(
2929

3030
const reporter = reporterFactory(configuration);
3131

32-
rpcService.addCallHandler(getReport, async (change) => {
32+
rpcService.addCallHandler(getReport, async ({ change, watching }) => {
3333
if (report) {
3434
throw new Error(`Close previous report before opening the next one.`);
3535
}
3636

37-
report = await reporter.getReport(change);
37+
report = await reporter.getReport(change, watching);
3838
});
3939
rpcService.addCallHandler(getDependencies, () => {
4040
if (!report) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as ts from 'typescript';
2+
import { TypeScriptHostExtension } from '../extension/TypeScriptExtension';
3+
import { ControlledTypeScriptSystem } from './ControlledTypeScriptSystem';
4+
5+
function createControlledCompilerHost(
6+
typescript: typeof ts,
7+
parsedCommandLine: ts.ParsedCommandLine,
8+
system: ControlledTypeScriptSystem,
9+
hostExtensions: TypeScriptHostExtension[] = []
10+
): ts.CompilerHost {
11+
const baseCompilerHost = typescript.createCompilerHost(parsedCommandLine.options);
12+
13+
let controlledCompilerHost: ts.CompilerHost = {
14+
...baseCompilerHost,
15+
fileExists: system.fileExists,
16+
readFile: system.readFile,
17+
directoryExists: system.directoryExists,
18+
getDirectories: system.getDirectories,
19+
realpath: system.realpath,
20+
};
21+
22+
hostExtensions.forEach((hostExtension) => {
23+
if (hostExtension.extendCompilerHost) {
24+
controlledCompilerHost = hostExtension.extendCompilerHost(
25+
controlledCompilerHost,
26+
parsedCommandLine
27+
);
28+
}
29+
});
30+
31+
return controlledCompilerHost;
32+
}
33+
34+
export { createControlledCompilerHost };

src/typescript-reporter/reporter/TypeScriptReporter.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from './TypeScriptConfigurationParser';
1818
import { createPerformance } from '../../profile/Performance';
1919
import { connectTypeScriptPerformance } from '../profile/TypeScriptPerformance';
20+
import { createControlledCompilerHost } from './ControlledCompilerHost';
2021

2122
// write this type as it's available only in the newest TypeScript versions (^4.1.0)
2223
interface Tracing {
@@ -30,12 +31,14 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
3031
let parseConfigurationDiagnostics: ts.Diagnostic[] = [];
3132
let dependencies: Dependencies | undefined;
3233
let configurationChanged = false;
34+
let compilerHost: ts.CompilerHost | undefined;
3335
let watchCompilerHost:
3436
| ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.SemanticDiagnosticsBuilderProgram>
3537
| undefined;
3638
let watchSolutionBuilderHost:
3739
| ts.SolutionBuilderWithWatchHost<ts.SemanticDiagnosticsBuilderProgram>
3840
| undefined;
41+
let program: ts.Program | undefined;
3942
let watchProgram:
4043
| ts.WatchOfFilesAndCompilerOptions<ts.SemanticDiagnosticsBuilderProgram>
4144
| undefined;
@@ -69,27 +72,27 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
6972
return (typescript as any).tracing;
7073
}
7174

72-
function getDiagnosticsOfBuilderProgram(builderProgram: ts.BuilderProgram) {
75+
function getDiagnosticsOfProgram(program: ts.Program | ts.BuilderProgram) {
7376
const diagnostics: ts.Diagnostic[] = [];
7477

7578
if (configuration.diagnosticOptions.syntactic) {
7679
performance.markStart('Syntactic Diagnostics');
77-
diagnostics.push(...builderProgram.getSyntacticDiagnostics());
80+
diagnostics.push(...program.getSyntacticDiagnostics());
7881
performance.markEnd('Syntactic Diagnostics');
7982
}
8083
if (configuration.diagnosticOptions.global) {
8184
performance.markStart('Global Diagnostics');
82-
diagnostics.push(...builderProgram.getGlobalDiagnostics());
85+
diagnostics.push(...program.getGlobalDiagnostics());
8386
performance.markEnd('Global Diagnostics');
8487
}
8588
if (configuration.diagnosticOptions.semantic) {
8689
performance.markStart('Semantic Diagnostics');
87-
diagnostics.push(...builderProgram.getSemanticDiagnostics());
90+
diagnostics.push(...program.getSemanticDiagnostics());
8891
performance.markEnd('Semantic Diagnostics');
8992
}
9093
if (configuration.diagnosticOptions.declaration) {
9194
performance.markStart('Declaration Diagnostics');
92-
diagnostics.push(...builderProgram.getDeclarationDiagnostics());
95+
diagnostics.push(...program.getDeclarationDiagnostics());
9396
performance.markEnd('Declaration Diagnostics');
9497
}
9598

@@ -221,7 +224,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
221224
}
222225

223226
return {
224-
getReport: async ({ changedFiles = [], deletedFiles = [] }) => {
227+
getReport: async ({ changedFiles = [], deletedFiles = [] }, watching) => {
225228
// clear cache to be ready for next iteration and to free memory
226229
system.clearCache();
227230

@@ -233,8 +236,10 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
233236
// we need to re-create programs
234237
parsedConfiguration = undefined;
235238
dependencies = undefined;
239+
compilerHost = undefined;
236240
watchCompilerHost = undefined;
237241
watchSolutionBuilderHost = undefined;
242+
program = undefined;
238243
watchProgram = undefined;
239244
solutionBuilder = undefined;
240245

@@ -346,7 +351,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
346351
undefined,
347352
(builderProgram) => {
348353
const projectName = getProjectNameOfBuilderProgram(builderProgram);
349-
const diagnostics = getDiagnosticsOfBuilderProgram(builderProgram);
354+
const diagnostics = getDiagnosticsOfProgram(builderProgram);
350355

351356
// update diagnostics
352357
diagnosticsPerProject.set(projectName, diagnostics);
@@ -379,7 +384,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
379384
solutionBuilder.build();
380385
performance.markEnd('Build Solutions');
381386
}
382-
} else {
387+
} else if (watching) {
383388
// watch compiler case
384389
// ensure watch compiler host exists
385390
if (!watchCompilerHost) {
@@ -412,7 +417,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
412417
undefined,
413418
(builderProgram) => {
414419
const projectName = getProjectNameOfBuilderProgram(builderProgram);
415-
const diagnostics = getDiagnosticsOfBuilderProgram(builderProgram);
420+
const diagnostics = getDiagnosticsOfProgram(builderProgram);
416421

417422
// update diagnostics
418423
diagnosticsPerProject.set(projectName, diagnostics);
@@ -440,6 +445,28 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
440445
watchProgram.updateRootFileNames(dependencies.files);
441446
shouldUpdateRootFiles = false;
442447
}
448+
} else {
449+
if (!compilerHost) {
450+
compilerHost = createControlledCompilerHost(
451+
typescript,
452+
parsedConfiguration,
453+
system,
454+
extensions
455+
);
456+
}
457+
if (!program) {
458+
program = ts.createProgram({
459+
rootNames: parsedConfiguration.fileNames,
460+
options: parsedConfiguration.options,
461+
projectReferences: parsedConfiguration.projectReferences,
462+
host: compilerHost,
463+
});
464+
}
465+
const diagnostics = getDiagnosticsOfProgram(program);
466+
const projectName = getConfigFilePathFromCompilerOptions(program.getCompilerOptions());
467+
468+
// update diagnostics
469+
diagnosticsPerProject.set(projectName, diagnostics);
443470
}
444471

445472
changedFiles.forEach((changedFile) => {

0 commit comments

Comments
 (0)