Skip to content

Commit

Permalink
feat(apiincrementalchecker): improve generation of diagnostics
Browse files Browse the repository at this point in the history
when the options `checkSyntacticErrors: false` and `useTypescriptIncrementalApi: true` both were active, no semantic errors were emitted as soon as the first syntactic error was encountered
this patches the `typescript` import to override that behavior

see discussion in #257
  • Loading branch information
phryneas committed Apr 23, 2019
1 parent d078278 commit ae80e5f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
64 changes: 64 additions & 0 deletions src/patchTypescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// tslint:disable-next-line:no-implicit-dependencies
import * as ts from 'typescript'; // Imported for types alone

export interface TypeScriptPatchConfig {
/**
* Ususally, the compilerHost created with typescript.createWatchCompilerHost will bail out of diagnostics collection if there has been any syntactic error.
* (see [`emitFilesAndReportErrors`](https://github.com/Microsoft/TypeScript/blob/89386ddda7dafc63cb35560e05412487f47cc267/src/compiler/watch.ts#L141) )
* If this plugin is running with `checkSyntacticErrors: false`, this might lead to situations where no syntactic errors are reported within webpack
* (because the file causing a syntactic error might not get processed by ts-loader), but there are semantic errors that would be missed due to this behavior.
* This ensures that the compilerHost always assumes that there were no syntactic errors to be found and continues to check for semantic errors.
*/
skipGetSyntacticDiagnostics: boolean;
}

/**
* While it is often possible to pass a wrapped or modified copy of `typescript` or `typescript.sys` as a function argument to override/extend some typescript-internal behavior,
* sometimes the typescript-internal code ignores these passed objects and directly references the internal `typescript` object reference.
* In these situations, the only way of consistently overriding some behavior is to directly replace methods on the `typescript` object.
*
* So beware, this method directly modifies the passed `typescript` object!
* @param typescript TypeScript instance to patch
* @param config
*/
export function patchTypescript(
typescript: typeof ts,
config: TypeScriptPatchConfig
) {
if (config.skipGetSyntacticDiagnostics) {
patchSkipGetSyntacticDiagnostics(typescript);
}
}

/**
* Overrides the [`typescript.createEmitAndSemanticDiagnosticsBuilderProgram`](https://github.com/Microsoft/TypeScript/blob/89386ddda7dafc63cb35560e05412487f47cc267/src/compiler/builder.ts#L1176)
* method to return a `ts.Program` instance that does not emit syntactic errors,
* to prevent the [`typescript.createWatchCompilerHost`](https://github.com/Microsoft/TypeScript/blob/89386ddda7dafc63cb35560e05412487f47cc267/src/compiler/watch.ts#L333)
* method from bailing during diagnostic collection in the [`emitFilesAndReportErrors`](https://github.com/Microsoft/TypeScript/blob/89386ddda7dafc63cb35560e05412487f47cc267/src/compiler/watch.ts#L141) callback.
*
* See the description of TypeScriptPatchConfig.skipGetSyntacticDiagnostics and
* [this github discussion](https://github.com/Realytics/fork-ts-checker-webpack-plugin/issues/257#issuecomment-485414182)
* for further information on this problem & solution.
*/
function patchSkipGetSyntacticDiagnostics(typescript: typeof ts) {
const {
createEmitAndSemanticDiagnosticsBuilderProgram: originalCreateEmitAndSemanticDiagnosticsBuilderProgram
} = typescript;

const patchedMethods: Pick<
typeof ts,
'createEmitAndSemanticDiagnosticsBuilderProgram'
> = {
createEmitAndSemanticDiagnosticsBuilderProgram(...args: any[]) {
const program = originalCreateEmitAndSemanticDiagnosticsBuilderProgram.apply(
typescript,
args as any
);
program.getSyntacticDiagnostics = () => [];
return program;
}
};

// directly patch the typescript object!
Object.assign(typescript, patchedMethods);
}
8 changes: 8 additions & 0 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from './NormalizedMessageFactories';
import { RpcProvider } from 'worker-rpc';
import { RunPayload, RunResult, RUN } from './RpcTypes';
import { TypeScriptPatchConfig, patchTypescript } from './patchTypescript';

const rpc = new RpcProvider(message => {
try {
Expand All @@ -25,6 +26,13 @@ const rpc = new RpcProvider(message => {
process.on('message', message => rpc.dispatch(message));

const typescript: typeof ts = require(process.env.TYPESCRIPT_PATH!);
const patchConfig: TypeScriptPatchConfig = {
skipGetSyntacticDiagnostics:
process.env.USE_INCREMENTAL_API === 'true' &&
process.env.CHECK_SYNTACTIC_ERRORS !== 'true'
};

patchTypescript(typescript, patchConfig);

// message factories
export const createNormalizedMessageFromDiagnostic = makeCreateNormalizedMessageFromDiagnostic(
Expand Down

0 comments on commit ae80e5f

Please sign in to comment.