Skip to content

Commit

Permalink
feat: use simple program for single compilation (no watch) (#574)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
piotr-oles committed Mar 5, 2021
1 parent 1057b23 commit a195dd5
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/hooks/tapStartToConnectAndRunReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function tapStartToConnectAndRunReporter(
await previousReport.close();
}

const report = await reporter.getReport(change);
const report = await reporter.getReport(change, state.watching);
resolve(report);

report
Expand Down
6 changes: 3 additions & 3 deletions src/reporter/AggregatedReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function createAggregatedReporter<TReporter extends Reporter>(reporter: TReporte

const aggregatedReporter: TReporter = {
...reporter,
getReport: async (change) => {
getReport: async (change, watching) => {
if (!pendingPromise) {
let resolvePending: () => void;
pendingPromise = new Promise((resolve) => {
Expand All @@ -23,7 +23,7 @@ function createAggregatedReporter<TReporter extends Reporter>(reporter: TReporte
});

return reporter
.getReport(change)
.getReport(change, watching)
.then((report) => ({
...report,
async close() {
Expand All @@ -45,7 +45,7 @@ function createAggregatedReporter<TReporter extends Reporter>(reporter: TReporte
const change = aggregateFilesChanges(queuedChanges);
queuedChanges = [];

return aggregatedReporter.getReport(change);
return aggregatedReporter.getReport(change, watching);
} else {
throw new OperationCanceledError('getReport canceled - new report requested.');
}
Expand Down
2 changes: 1 addition & 1 deletion src/reporter/Reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FilesChange } from './FilesChange';
import { Report } from './Report';

interface Reporter {
getReport(change: FilesChange): Promise<Report>;
getReport(change: FilesChange, watching: boolean): Promise<Report>;
}

export { Reporter };
8 changes: 4 additions & 4 deletions src/reporter/reporter-rpc/ReporterRpcClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ function createReporterRpcClient<TConfiguration extends object>(
await channel.close();
}
},
getReport: async (change) => {
const reportId = await rpcClient.dispatchCall(getReport, change);
getReport: async (change, watching) => {
const reportId = await rpcClient.dispatchCall(getReport, { change, watching });

return {
getDependencies() {
Expand All @@ -65,8 +65,8 @@ function composeReporterRpcClients(clients: ReporterRpcClient[]): ReporterRpcCli
connect: () => Promise.all(clients.map((client) => client.connect())).then(() => undefined),
disconnect: () =>
Promise.all(clients.map((client) => client.disconnect())).then(() => undefined),
getReport: (change: FilesChange) =>
Promise.all(clients.map((client) => client.getReport(change))).then((reports) => ({
getReport: (change: FilesChange, watching: boolean) =>
Promise.all(clients.map((client) => client.getReport(change, watching))).then((reports) => ({
getDependencies: () =>
Promise.all(reports.map((report) => report.getDependencies())).then((dependencies) =>
dependencies.reduce(
Expand Down
2 changes: 1 addition & 1 deletion src/reporter/reporter-rpc/ReporterRpcProcedure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Issue } from '../../issue';
import { Dependencies } from '../Dependencies';

const configure: RpcProcedure<object, void> = 'configure';
const getReport: RpcProcedure<FilesChange, void> = 'getReport';
const getReport: RpcProcedure<{ change: FilesChange; watching: boolean }, void> = 'getReport';
const getDependencies: RpcProcedure<void, Dependencies> = 'getDependencies';
const getIssues: RpcProcedure<void, Issue[]> = 'getIssues';
const closeReport: RpcProcedure<void, void> = 'closeReport';
Expand Down
4 changes: 2 additions & 2 deletions src/reporter/reporter-rpc/ReporterRpcService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ function registerReporterRpcService<TConfiguration extends object>(

const reporter = reporterFactory(configuration);

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

report = await reporter.getReport(change);
report = await reporter.getReport(change, watching);
});
rpcService.addCallHandler(getDependencies, () => {
if (!report) {
Expand Down
34 changes: 34 additions & 0 deletions src/typescript-reporter/reporter/ControlledCompilerHost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as ts from 'typescript';
import { TypeScriptHostExtension } from '../extension/TypeScriptExtension';
import { ControlledTypeScriptSystem } from './ControlledTypeScriptSystem';

function createControlledCompilerHost(
typescript: typeof ts,
parsedCommandLine: ts.ParsedCommandLine,
system: ControlledTypeScriptSystem,
hostExtensions: TypeScriptHostExtension[] = []
): ts.CompilerHost {
const baseCompilerHost = typescript.createCompilerHost(parsedCommandLine.options);

let controlledCompilerHost: ts.CompilerHost = {
...baseCompilerHost,
fileExists: system.fileExists,
readFile: system.readFile,
directoryExists: system.directoryExists,
getDirectories: system.getDirectories,
realpath: system.realpath,
};

hostExtensions.forEach((hostExtension) => {
if (hostExtension.extendCompilerHost) {
controlledCompilerHost = hostExtension.extendCompilerHost(
controlledCompilerHost,
parsedCommandLine
);
}
});

return controlledCompilerHost;
}

export { createControlledCompilerHost };
45 changes: 36 additions & 9 deletions src/typescript-reporter/reporter/TypeScriptReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from './TypeScriptConfigurationParser';
import { createPerformance } from '../../profile/Performance';
import { connectTypeScriptPerformance } from '../profile/TypeScriptPerformance';
import { createControlledCompilerHost } from './ControlledCompilerHost';

// write this type as it's available only in the newest TypeScript versions (^4.1.0)
interface Tracing {
Expand All @@ -30,12 +31,14 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
let parseConfigurationDiagnostics: ts.Diagnostic[] = [];
let dependencies: Dependencies | undefined;
let configurationChanged = false;
let compilerHost: ts.CompilerHost | undefined;
let watchCompilerHost:
| ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.SemanticDiagnosticsBuilderProgram>
| undefined;
let watchSolutionBuilderHost:
| ts.SolutionBuilderWithWatchHost<ts.SemanticDiagnosticsBuilderProgram>
| undefined;
let program: ts.Program | undefined;
let watchProgram:
| ts.WatchOfFilesAndCompilerOptions<ts.SemanticDiagnosticsBuilderProgram>
| undefined;
Expand Down Expand Up @@ -69,27 +72,27 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
return (typescript as any).tracing;
}

function getDiagnosticsOfBuilderProgram(builderProgram: ts.BuilderProgram) {
function getDiagnosticsOfProgram(program: ts.Program | ts.BuilderProgram) {
const diagnostics: ts.Diagnostic[] = [];

if (configuration.diagnosticOptions.syntactic) {
performance.markStart('Syntactic Diagnostics');
diagnostics.push(...builderProgram.getSyntacticDiagnostics());
diagnostics.push(...program.getSyntacticDiagnostics());
performance.markEnd('Syntactic Diagnostics');
}
if (configuration.diagnosticOptions.global) {
performance.markStart('Global Diagnostics');
diagnostics.push(...builderProgram.getGlobalDiagnostics());
diagnostics.push(...program.getGlobalDiagnostics());
performance.markEnd('Global Diagnostics');
}
if (configuration.diagnosticOptions.semantic) {
performance.markStart('Semantic Diagnostics');
diagnostics.push(...builderProgram.getSemanticDiagnostics());
diagnostics.push(...program.getSemanticDiagnostics());
performance.markEnd('Semantic Diagnostics');
}
if (configuration.diagnosticOptions.declaration) {
performance.markStart('Declaration Diagnostics');
diagnostics.push(...builderProgram.getDeclarationDiagnostics());
diagnostics.push(...program.getDeclarationDiagnostics());
performance.markEnd('Declaration Diagnostics');
}

Expand Down Expand Up @@ -221,7 +224,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
}

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

Expand All @@ -233,8 +236,10 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
// we need to re-create programs
parsedConfiguration = undefined;
dependencies = undefined;
compilerHost = undefined;
watchCompilerHost = undefined;
watchSolutionBuilderHost = undefined;
program = undefined;
watchProgram = undefined;
solutionBuilder = undefined;

Expand Down Expand Up @@ -346,7 +351,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
undefined,
(builderProgram) => {
const projectName = getProjectNameOfBuilderProgram(builderProgram);
const diagnostics = getDiagnosticsOfBuilderProgram(builderProgram);
const diagnostics = getDiagnosticsOfProgram(builderProgram);

// update diagnostics
diagnosticsPerProject.set(projectName, diagnostics);
Expand Down Expand Up @@ -379,7 +384,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
solutionBuilder.build();
performance.markEnd('Build Solutions');
}
} else {
} else if (watching) {
// watch compiler case
// ensure watch compiler host exists
if (!watchCompilerHost) {
Expand Down Expand Up @@ -412,7 +417,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
undefined,
(builderProgram) => {
const projectName = getProjectNameOfBuilderProgram(builderProgram);
const diagnostics = getDiagnosticsOfBuilderProgram(builderProgram);
const diagnostics = getDiagnosticsOfProgram(builderProgram);

// update diagnostics
diagnosticsPerProject.set(projectName, diagnostics);
Expand Down Expand Up @@ -440,6 +445,28 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration
watchProgram.updateRootFileNames(dependencies.files);
shouldUpdateRootFiles = false;
}
} else {
if (!compilerHost) {
compilerHost = createControlledCompilerHost(
typescript,
parsedConfiguration,
system,
extensions
);
}
if (!program) {
program = ts.createProgram({
rootNames: parsedConfiguration.fileNames,
options: parsedConfiguration.options,
projectReferences: parsedConfiguration.projectReferences,
host: compilerHost,
});
}
const diagnostics = getDiagnosticsOfProgram(program);
const projectName = getConfigFilePathFromCompilerOptions(program.getCompilerOptions());

// update diagnostics
diagnosticsPerProject.set(projectName, diagnostics);
}

changedFiles.forEach((changedFile) => {
Expand Down

0 comments on commit a195dd5

Please sign in to comment.