diff --git a/src/hooks/tapStartToConnectAndRunReporter.ts b/src/hooks/tapStartToConnectAndRunReporter.ts index 0929ac31..a27a1d08 100644 --- a/src/hooks/tapStartToConnectAndRunReporter.ts +++ b/src/hooks/tapStartToConnectAndRunReporter.ts @@ -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 diff --git a/src/reporter/AggregatedReporter.ts b/src/reporter/AggregatedReporter.ts index 72143cae..b8a2cf24 100644 --- a/src/reporter/AggregatedReporter.ts +++ b/src/reporter/AggregatedReporter.ts @@ -12,7 +12,7 @@ function createAggregatedReporter(reporter: TReporte const aggregatedReporter: TReporter = { ...reporter, - getReport: async (change) => { + getReport: async (change, watching) => { if (!pendingPromise) { let resolvePending: () => void; pendingPromise = new Promise((resolve) => { @@ -23,7 +23,7 @@ function createAggregatedReporter(reporter: TReporte }); return reporter - .getReport(change) + .getReport(change, watching) .then((report) => ({ ...report, async close() { @@ -45,7 +45,7 @@ function createAggregatedReporter(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.'); } diff --git a/src/reporter/Reporter.ts b/src/reporter/Reporter.ts index 2435f770..332e9b10 100644 --- a/src/reporter/Reporter.ts +++ b/src/reporter/Reporter.ts @@ -2,7 +2,7 @@ import { FilesChange } from './FilesChange'; import { Report } from './Report'; interface Reporter { - getReport(change: FilesChange): Promise; + getReport(change: FilesChange, watching: boolean): Promise; } export { Reporter }; diff --git a/src/reporter/reporter-rpc/ReporterRpcClient.ts b/src/reporter/reporter-rpc/ReporterRpcClient.ts index fc054fe9..36a5e829 100644 --- a/src/reporter/reporter-rpc/ReporterRpcClient.ts +++ b/src/reporter/reporter-rpc/ReporterRpcClient.ts @@ -41,8 +41,8 @@ function createReporterRpcClient( 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() { @@ -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( diff --git a/src/reporter/reporter-rpc/ReporterRpcProcedure.ts b/src/reporter/reporter-rpc/ReporterRpcProcedure.ts index 7c17c1ca..8742c057 100644 --- a/src/reporter/reporter-rpc/ReporterRpcProcedure.ts +++ b/src/reporter/reporter-rpc/ReporterRpcProcedure.ts @@ -4,7 +4,7 @@ import { Issue } from '../../issue'; import { Dependencies } from '../Dependencies'; const configure: RpcProcedure = 'configure'; -const getReport: RpcProcedure = 'getReport'; +const getReport: RpcProcedure<{ change: FilesChange; watching: boolean }, void> = 'getReport'; const getDependencies: RpcProcedure = 'getDependencies'; const getIssues: RpcProcedure = 'getIssues'; const closeReport: RpcProcedure = 'closeReport'; diff --git a/src/reporter/reporter-rpc/ReporterRpcService.ts b/src/reporter/reporter-rpc/ReporterRpcService.ts index 0622bd50..6cc0c7e7 100644 --- a/src/reporter/reporter-rpc/ReporterRpcService.ts +++ b/src/reporter/reporter-rpc/ReporterRpcService.ts @@ -29,12 +29,12 @@ function registerReporterRpcService( 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) { diff --git a/src/typescript-reporter/reporter/ControlledCompilerHost.ts b/src/typescript-reporter/reporter/ControlledCompilerHost.ts new file mode 100644 index 00000000..eed02d61 --- /dev/null +++ b/src/typescript-reporter/reporter/ControlledCompilerHost.ts @@ -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 }; diff --git a/src/typescript-reporter/reporter/TypeScriptReporter.ts b/src/typescript-reporter/reporter/TypeScriptReporter.ts index ffced91e..0df3038f 100644 --- a/src/typescript-reporter/reporter/TypeScriptReporter.ts +++ b/src/typescript-reporter/reporter/TypeScriptReporter.ts @@ -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 { @@ -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 | undefined; let watchSolutionBuilderHost: | ts.SolutionBuilderWithWatchHost | undefined; + let program: ts.Program | undefined; let watchProgram: | ts.WatchOfFilesAndCompilerOptions | undefined; @@ -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'); } @@ -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(); @@ -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; @@ -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); @@ -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) { @@ -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); @@ -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) => {