Create api with watchHost to include in solution builder host

This commit is contained in:
Sheetal Nandi 2018-08-20 12:52:09 -07:00
parent 0c4003e735
commit 26b4b6c9ad
6 changed files with 94 additions and 54 deletions

View file

@ -394,6 +394,9 @@ namespace ts {
deleteFile(fileName: string): void;
}
export interface SolutionBuilderWithWatchHost extends SolutionBuilderHost, WatchHost {
}
export function createSolutionBuilderHost(system = sys) {
const host = createCompilerHost({}, /*setParentNodes*/ undefined, system) as SolutionBuilderHost;
host.getModifiedTime = system.getModifiedTime ? path => system.getModifiedTime!(path) : () => undefined;
@ -402,12 +405,25 @@ namespace ts {
return host;
}
export function createSolutionBuilderWithWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter) {
const host = createSolutionBuilderHost(system) as SolutionBuilderWithWatchHost;
const watchHost = createWatchHost(system, reportWatchStatus);
host.onWatchStatusChange = watchHost.onWatchStatusChange;
host.watchFile = watchHost.watchFile;
host.watchDirectory = watchHost.watchDirectory;
host.setTimeout = watchHost.setTimeout;
host.clearTimeout = watchHost.clearTimeout;
return host;
}
/**
* A SolutionBuilder has an immutable set of rootNames that are the "entry point" projects, but
* can dynamically add/remove other projects based on changes on the rootNames' references
* TODO: use SolutionBuilderWithWatchHost => watchedSolution
* use SolutionBuilderHost => Solution
*/
export function createSolutionBuilder(host: SolutionBuilderHost, buildHost: BuildHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions, system?: System) {
export function createSolutionBuilder(host: SolutionBuilderHost, buildHost: BuildHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions) {
const hostWithWatch = host as SolutionBuilderWithWatchHost;
const configFileCache = createConfigFileCache(host);
let context = createBuildContext(defaultOptions);
@ -430,9 +446,6 @@ namespace ts {
};
function startWatching() {
if (!system) throw new Error("System host must be provided if using --watch");
if (!system.watchFile || !system.watchDirectory || !system.setTimeout) throw new Error("System host must support watchFile / watchDirectory / setTimeout if using --watch");
const graph = getGlobalDependencyGraph()!;
if (!graph.buildQueue) {
// Everything is broken - we don't even know what to watch. Give up.
@ -443,7 +456,7 @@ namespace ts {
const cfg = configFileCache.parseConfigFile(resolved);
if (cfg) {
// Watch this file
system.watchFile(resolved, () => {
hostWithWatch.watchFile(resolved, () => {
configFileCache.removeKey(resolved);
invalidateProjectAndScheduleBuilds(resolved);
});
@ -451,7 +464,7 @@ namespace ts {
// Update watchers for wildcard directories
if (cfg.configFileSpecs) {
updateWatchingWildcardDirectories(existingWatchersForWildcards, createMapFromTemplate(cfg.configFileSpecs.wildcardDirectories), (dir, flags) => {
return system.watchDirectory!(dir, () => {
return hostWithWatch.watchDirectory(dir, () => {
invalidateProjectAndScheduleBuilds(resolved);
}, !!(flags & WatchDirectoryFlags.Recursive));
});
@ -459,7 +472,7 @@ namespace ts {
// Watch input files
for (const input of cfg.fileNames) {
system.watchFile(input, () => {
hostWithWatch.watchFile(input, () => {
invalidateProjectAndScheduleBuilds(resolved);
});
}
@ -468,8 +481,11 @@ namespace ts {
function invalidateProjectAndScheduleBuilds(resolved: ResolvedConfigFileName) {
invalidateProject(resolved);
system!.setTimeout!(buildInvalidatedProjects, 100);
system!.setTimeout!(buildDependentInvalidatedProjects, 3000);
if (!hostWithWatch.setTimeout) {
return;
}
hostWithWatch.setTimeout(buildInvalidatedProjects, 100);
hostWithWatch.setTimeout(buildDependentInvalidatedProjects, 3000);
}
}

View file

@ -174,6 +174,17 @@ namespace ts {
const noopFileWatcher: FileWatcher = { close: noop };
export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
return {
onWatchStatusChange,
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop
};
}
/**
* Creates the watch compiler host that can be extended with config file or root file names and options host
*/
@ -186,7 +197,7 @@ namespace ts {
host; // tslint:disable-line no-unused-expression (TODO: `host` is unused!)
const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames;
const writeFileName = (s: string) => system.write(s + system.newLine);
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
const { onWatchStatusChange, watchFile, watchDirectory, setTimeout, clearTimeout } = createWatchHost(system, reportWatchStatus);
return {
useCaseSensitiveFileNames,
getNewLine: () => system.newLine,
@ -200,10 +211,10 @@ namespace ts {
readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth),
realpath: system.realpath && (path => system.realpath!(path)),
getEnvironmentVariable: system.getEnvironmentVariable && (name => system.getEnvironmentVariable(name)),
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop,
watchFile,
watchDirectory,
setTimeout,
clearTimeout,
trace: s => system.write(s),
onWatchStatusChange,
createDirectory: path => system.createDirectory(path),
@ -224,10 +235,10 @@ namespace ts {
const reportSummary = (errorCount: number) => {
if (errorCount === 1) {
onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
}
else {
onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
}
};
@ -270,7 +281,21 @@ namespace ts {
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
export interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
export interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
export interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
// TODO: GH#18217 Optional methods are frequently asserted
/**
@ -279,8 +304,6 @@ namespace ts {
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
// Only for testing
/*@internal*/
@ -323,15 +346,6 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/** Internal interface used to wire emit through same host */

View file

@ -1,7 +1,7 @@
namespace ts.tscWatch {
export import libFile = TestFSWithWatch.libFile;
function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const host = createSolutionBuilderHost(system);
const host = createSolutionBuilderWithWatchHost(system);
const reportDiag = createDiagnosticReporter(system);
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiag(createCompilerDiagnostic(message, ...args));
const buildHost: BuildHost = {
@ -10,7 +10,7 @@ namespace ts.tscWatch {
message: report,
errorDiagnostic: d => reportDiag(d)
};
return ts.createSolutionBuilder(host, buildHost, rootNames, defaultOptions || { dry: false, force: false, verbose: false }, system);
return ts.createSolutionBuilder(host, buildHost, rootNames, defaultOptions || { dry: false, force: false, verbose: false, watch: true });
}
function createSolutionBuilderWithWatch(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {

View file

@ -247,6 +247,9 @@ namespace ts {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.watch) {
reportWatchModeWithoutSysSupport();
}
// Nonsensical combinations
if (buildOptions.clean && buildOptions.force) {
@ -279,7 +282,8 @@ namespace ts {
errorDiagnostic: d => reportDiagnostic(d)
};
const builder = createSolutionBuilder(createSolutionBuilderHost(), buildHost, projects, buildOptions);
// TODO: change this to host if watch => watchHost otherwiue without wathc
const builder = createSolutionBuilder(createSolutionBuilderWithWatchHost(), buildHost, projects, buildOptions);
if (buildOptions.clean) {
return builder.cleanAllProjects();
}

View file

@ -4309,15 +4309,26 @@ declare namespace ts {
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
getCurrentDirectory(): string;
@ -4350,14 +4361,6 @@ declare namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/**
* Host to create watch with root files and options

View file

@ -4309,15 +4309,26 @@ declare namespace ts {
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
getCurrentDirectory(): string;
@ -4350,14 +4361,6 @@ declare namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/**
* Host to create watch with root files and options