import EventEmitter from 'events'; import ts from 'typescript'; const formatHost: ts.FormatDiagnosticsHost = { getCanonicalFileName: (path) => path, getCurrentDirectory: ts.sys.getCurrentDirectory, getNewLine: () => ts.sys.newLine, }; export function tsWatcher(configFileName: string, optionsToExtend: ts.CompilerOptions | undefined): EventEmitter; export function tsWatcher(rootFiles: string[], options: ts.CompilerOptions): EventEmitter; export function tsWatcher(rootFiles, options) { // TypeScript can use several different program creation "strategies": // * ts.createEmitAndSemanticDiagnosticsBuilderProgram, // * ts.createSemanticDiagnosticsBuilderProgram // * ts.createAbstractBuilder // The first two produce "builder programs". These use an incremental strategy // to only re-check and emit files whose contents may have changed, or whose // dependencies may have changes which may impact change the result of prior // type-check and emit. // The last uses an ordinary program which does a full type check after every // change. // Between `createEmitAndSemanticDiagnosticsBuilderProgram` and // `createSemanticDiagnosticsBuilderProgram`, the only difference is emit. // For pure type-checking scenarios, or when another tool/process handles emit, // using `createSemanticDiagnosticsBuilderProgram` may be more desirable. const createProgram = ts.createSemanticDiagnosticsBuilderProgram; // Note that there is another overload for `createWatchCompilerHost` that takes // a set of root files. const host = ts.createWatchCompilerHost( rootFiles, options, ts.sys, createProgram, reportDiagnostic, reportWatchStatusChanged, ); const emitter = new EventEmitter(); // You can technically override any given hook on the host, though you probably // don't need to. // Note that we're assuming `origCreateProgram` and `origPostProgramCreate` // doesn't use `this` at all. // const origCreateProgram = host.createProgram; // host.createProgram = (...args) => { // console.log('** We\'re about to create the program! **'); // emitter.emit('createProgram', ...args); // return origCreateProgram(...args); // }; const origPostProgramCreate = host.afterProgramCreate; host.afterProgramCreate = (program) => { // program. console.log('** We finished making the program! **'); emitter.emit('afterProgramCreate', program); origPostProgramCreate!(program); }; setImmediate(() => { // `createWatchProgram` creates an initial program, watches files, and updates // the program over time. ts.createWatchProgram(host); }); return emitter; } function reportDiagnostic(diagnostic: ts.Diagnostic) { console.error( 'Error', diagnostic.code, ':', ts.flattenDiagnosticMessageText( diagnostic.messageText, formatHost.getNewLine(), ), ); } /** * Prints a diagnostic every time the watch status changes. * This is mainly for messages like "Starting compilation" or "Compilation completed". */ function reportWatchStatusChanged(diagnostic: ts.Diagnostic) { console.info(ts.formatDiagnostic(diagnostic, formatHost)); }