diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 80bd089429a..8ce51ac5e8c 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -228,19 +228,25 @@ namespace ts {
let output = "";
for (const diagnostic of diagnostics) {
- if (diagnostic.file) {
- const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
- const fileName = diagnostic.file.fileName;
- const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName));
- output += `${relativeFileName}(${line + 1},${character + 1}): `;
- }
-
- const category = DiagnosticCategory[diagnostic.category].toLowerCase();
- output += `${category} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`;
+ output += formatDiagnostic(diagnostic, host);
}
return output;
}
+ export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string {
+ const category = DiagnosticCategory[diagnostic.category].toLowerCase();
+ const errorMessage = `${category} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`;
+
+ if (diagnostic.file) {
+ const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
+ const fileName = diagnostic.file.fileName;
+ const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName));
+ return `${relativeFileName}(${line + 1},${character + 1}): ` + errorMessage;
+ }
+
+ return errorMessage;
+ }
+
const redForegroundEscapeSequence = "\u001b[91m";
const yellowForegroundEscapeSequence = "\u001b[93m";
const blueForegroundEscapeSequence = "\u001b[93m";
diff --git a/src/compiler/tscLib.ts b/src/compiler/tscLib.ts
index 36c9428cecd..bd4a64d8899 100644
--- a/src/compiler/tscLib.ts
+++ b/src/compiler/tscLib.ts
@@ -1,63 +1,13 @@
///
+///
///
namespace ts {
- export interface CompilerHost {
- /** If this is the emit based on the graph builder, use it to emit */
- emitWithBuilder?(program: Program): EmitResult;
- }
-
interface Statistic {
name: string;
value: string;
}
- export interface FormatDiagnosticsHostWithWrite extends FormatDiagnosticsHost {
- write?(s: string): void;
- }
-
- const defaultFormatDiagnosticsHost: FormatDiagnosticsHostWithWrite = sys ? {
- getCurrentDirectory: () => sys.getCurrentDirectory(),
- getNewLine: () => sys.newLine,
- getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames),
- write: s => sys.write(s)
- } : undefined;
-
- function getDefaultFormatDiagnosticsHost(system: System): FormatDiagnosticsHostWithWrite {
- return system === sys ? defaultFormatDiagnosticsHost : {
- getCurrentDirectory: () => system.getCurrentDirectory(),
- getNewLine: () => system.newLine,
- getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames),
- write: s => system.write(s)
- };
- }
-
- let reportDiagnosticWorker = reportDiagnosticSimply;
-
- function reportDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHostWithWrite) {
- reportDiagnosticWorker(diagnostic, host || defaultFormatDiagnosticsHost);
- }
-
- function reportDiagnostics(diagnostics: Diagnostic[], host: FormatDiagnosticsHostWithWrite): void {
- for (const diagnostic of diagnostics) {
- reportDiagnostic(diagnostic, host);
- }
- }
-
- function reportEmittedFiles(files: string[], system: System): void {
- if (!files || files.length === 0) {
- return;
- }
-
- const currentDir = system.getCurrentDirectory();
-
- for (const file of files) {
- const filepath = getNormalizedAbsolutePath(file, currentDir);
-
- system.write(`TSFILE: ${filepath}${system.newLine}`);
- }
- }
-
function countLines(program: Program): number {
let count = 0;
forEach(program.getSourceFiles(), file => {
@@ -71,25 +21,11 @@ namespace ts {
return diagnostic.messageText;
}
- function reportDiagnosticSimply(diagnostic: Diagnostic, host: FormatDiagnosticsHostWithWrite): void {
- host.write(ts.formatDiagnostics([diagnostic], host));
- }
-
- function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHostWithWrite): void {
- host.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + host.getNewLine());
- }
-
- function reportWatchDiagnostic(diagnostic: Diagnostic, system: System) {
- let output = new Date().toLocaleTimeString() + " - ";
-
- if (diagnostic.file) {
- const loc = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
- output += `${ diagnostic.file.fileName }(${ loc.line + 1 },${ loc.character + 1 }): `;
+ let reportDiagnostic = createDiagnosticReporter(sys, reportDiagnosticSimply);
+ function udpateReportDiagnostic(options: CompilerOptions) {
+ if (options.pretty) {
+ reportDiagnostic = createDiagnosticReporter(sys, reportDiagnosticWithColorAndContext);
}
-
- output += `${ flattenDiagnosticMessageText(diagnostic.messageText, system.newLine) }${ system.newLine + system.newLine + system.newLine }`;
-
- system.write(output);
}
function padLeft(s: string, length: number) {
@@ -118,7 +54,7 @@ namespace ts {
let configFileName: string;
if (commandLine.options.locale) {
if (!isJSONSupported()) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale"), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale"));
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors);
@@ -127,7 +63,7 @@ namespace ts {
// If there are any errors due to command line parsing and/or
// setting up localization, report them and quit.
if (commandLine.errors.length > 0) {
- reportDiagnostics(commandLine.errors, /*host*/ undefined);
+ reportDiagnostics(commandLine.errors, reportDiagnostic);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
@@ -149,11 +85,11 @@ namespace ts {
if (commandLine.options.project) {
if (!isJSONSupported()) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project"), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project"));
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
if (commandLine.fileNames.length !== 0) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line));
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
@@ -161,14 +97,14 @@ namespace ts {
if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) {
configFileName = combinePaths(fileOrDirectory, "tsconfig.json");
if (!sys.fileExists(configFileName)) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project));
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
}
else {
configFileName = fileOrDirectory;
if (!sys.fileExists(configFileName)) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project));
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
}
@@ -186,599 +122,70 @@ namespace ts {
const commandLineOptions = commandLine.options;
if (configFileName) {
- const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys);
+ const reportWatchDiagnostic = createWatchDiagnosticReporter();
+ const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys, reportDiagnostic, reportWatchDiagnostic);
+ udpateReportDiagnostic(configParseResult.options);
if (isWatchSet(configParseResult.options)) {
reportWatchModeWithoutSysSupport();
- createWatchModeWithConfigFile(configParseResult, commandLineOptions, sys);
+ createWatchModeWithConfigFile(configParseResult, commandLineOptions, createWatchingSystemHost(reportWatchDiagnostic));
}
else {
performCompilation(configParseResult.fileNames, configParseResult.options);
}
}
else {
- if (isWatchSet(commandLine.options)) {
+ udpateReportDiagnostic(commandLineOptions);
+ if (isWatchSet(commandLineOptions)) {
reportWatchModeWithoutSysSupport();
- createWatchModeWithoutConfigFile(commandLine.fileNames, commandLineOptions, sys);
+ createWatchModeWithoutConfigFile(commandLine.fileNames, commandLineOptions, createWatchingSystemHost());
}
else {
performCompilation(commandLine.fileNames, commandLineOptions);
}
}
+ }
- function reportWatchModeWithoutSysSupport() {
- if (!sys.watchFile || !sys.watchDirectory) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"), /* host */ undefined);
- sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
- }
- }
-
- function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions) {
- if (compilerOptions.pretty) {
- reportDiagnosticWorker = reportDiagnosticWithColorAndContext;
- }
-
- const compilerHost = createCompilerHost(compilerOptions);
- const compileResult = compile(rootFileNames, compilerOptions, compilerHost, sys);
- return sys.exit(compileResult.exitStatus);
+ function reportWatchModeWithoutSysSupport() {
+ if (!sys.watchFile || !sys.watchDirectory) {
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"));
+ sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
}
- interface HostFileInfo {
- version: number;
- sourceFile: SourceFile;
- fileWatcher: FileWatcher;
+ function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions) {
+ const compilerHost = createCompilerHost(compilerOptions);
+ enableStatistics(compilerOptions);
+
+ const program = createProgram(rootFileNames, compilerOptions, compilerHost);
+ const exitStatus = compileProgram(sys, program, () => program.emit(), reportDiagnostic);
+
+ reportStatistics(program);
+ return sys.exit(exitStatus);
}
- /* @internal */
- export function createWatchModeWithConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions, system: System) {
- return createWatchMode(configParseResult.fileNames, configParseResult.options, system, configParseResult.options.configFilePath, configParseResult.configFileSpecs, configParseResult.wildcardDirectories, optionsToExtend);
- }
-
- /* @internal */
- export function createWatchModeWithoutConfigFile(rootFileNames: string[], compilerOptions: CompilerOptions, system: System) {
- return createWatchMode(rootFileNames, compilerOptions, system);
- }
-
- function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, system: System, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike, optionsToExtendForConfigFile?: CompilerOptions) {
- let program: Program;
- let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
- let missingFilesMap: Map; // Map of file watchers for the missing files
- let configFileWatcher: FileWatcher; // watcher for the config file
- let watchedWildCardDirectories: Map; // map of watchers for the wild card directories in the config file
- let timerToUpdateProgram: any; // timer callback to recompile the program
-
- const sourceFilesCache = createMap(); // Cache that stores the source file and version info
-
- let host: System;
- if (configFileName) {
- host = createCachedSystem(system);
- configFileWatcher = system.watchFile(configFileName, onConfigFileChanged);
- }
- else {
- host = system;
- }
- const currentDirectory = host.getCurrentDirectory();
- const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
-
- // There is no extra check needed since we can just rely on the program to decide emit
- const builder = createBuilder(getCanonicalFileName, getFileEmitOutput, computeHash, _sourceFile => true);
-
- if (compilerOptions.pretty) {
- reportDiagnosticWorker = reportDiagnosticWithColorAndContext;
- }
-
- synchronizeProgram();
-
- // Update the wild card directory watch
- watchConfigFileWildCardDirectories();
-
- return () => program;
-
- function synchronizeProgram() {
- writeLog(`Synchronizing program`);
-
- if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists)) {
- return;
- }
-
- // Create the compiler host
- const compilerHost = createWatchedCompilerHost(compilerOptions);
- program = compile(rootFileNames, compilerOptions, compilerHost, system, program).program;
-
- // Update watches
- missingFilesMap = updateMissingFilePathsWatch(program, missingFilesMap, watchMissingFilePath, closeMissingFilePathWatcher);
-
- reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes), system);
- }
-
- function createWatchedCompilerHost(options: CompilerOptions): CompilerHost {
- const newLine = getNewLineCharacter(options, system);
- const realpath = host.realpath && ((path: string) => host.realpath(path));
-
- return {
- getSourceFile: getVersionedSourceFile,
- getSourceFileByPath: getVersionedSourceFileByPath,
- getDefaultLibLocation,
- getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
- writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { },
- getCurrentDirectory: memoize(() => host.getCurrentDirectory()),
- useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames,
- getCanonicalFileName,
- getNewLine: () => newLine,
- fileExists,
- readFile: fileName => host.readFile(fileName),
- trace: (s: string) => host.write(s + newLine),
- directoryExists: directoryName => host.directoryExists(directoryName),
- getEnvironmentVariable: name => host.getEnvironmentVariable ? host.getEnvironmentVariable(name) : "",
- getDirectories: (path: string) => host.getDirectories(path),
- realpath,
- onReleaseOldSourceFile,
- emitWithBuilder
- };
-
- // TODO: cache module resolution
- // if (host.resolveModuleNames) {
- // compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile);
- // }
- // if (host.resolveTypeReferenceDirectives) {
- // compilerHost.resolveTypeReferenceDirectives = (typeReferenceDirectiveNames, containingFile) => {
- // return host.resolveTypeReferenceDirectives(typeReferenceDirectiveNames, containingFile);
- // };
- // }
-
- function ensureDirectoriesExist(directoryPath: string) {
- if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) {
- const parentDirectory = getDirectoryPath(directoryPath);
- ensureDirectoriesExist(parentDirectory);
- host.createDirectory(directoryPath);
- }
- }
-
- function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
- try {
- performance.mark("beforeIOWrite");
- ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
-
- host.writeFile(fileName, data, writeByteOrderMark);
-
- performance.mark("afterIOWrite");
- performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
- }
- catch (e) {
- return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e);
- }
- }
-
- function emitWithBuilder(program: Program): EmitResult {
- builder.onProgramUpdateGraph(program);
- const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined;
- let sourceMaps: SourceMapData[];
- let emitSkipped: boolean;
- let diagnostics: Diagnostic[];
-
- const result = builder.emitChangedFiles(program);
- switch (result.length) {
- case 0:
- emitSkipped = true;
- break;
- case 1:
- const emitOutput = result[0];
- ({ diagnostics, sourceMaps, emitSkipped } = emitOutput);
- writeOutputFiles(emitOutput.outputFiles);
- break;
- default:
- for (const emitOutput of result) {
- if (emitOutput.emitSkipped) {
- emitSkipped = true;
- }
- diagnostics = concatenate(diagnostics, emitOutput.diagnostics);
- sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
- writeOutputFiles(emitOutput.outputFiles);
- }
- }
-
- return { emitSkipped, diagnostics: diagnostics || [], emittedFiles, sourceMaps };
-
- function writeOutputFiles(outputFiles: OutputFile[]) {
- if (outputFiles) {
- for (const outputFile of outputFiles) {
- const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark);
- if (error) {
- diagnostics.push(error);
- }
- if (emittedFiles) {
- emittedFiles.push(outputFile.name);
- }
- }
- }
- }
- }
- }
-
- function fileExists(fileName: string) {
- const path = toPath(fileName, currentDirectory, getCanonicalFileName);
- const hostSourceFileInfo = sourceFilesCache.get(path);
- if (hostSourceFileInfo !== undefined) {
- return !isString(hostSourceFileInfo);
- }
-
- return host.fileExists(fileName);
- }
-
- function getDefaultLibLocation(): string {
- return getDirectoryPath(normalizePath(host.getExecutingFilePath()));
- }
-
- function getVersionedSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
- return getVersionedSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), languageVersion, onError, shouldCreateNewSourceFile);
- }
-
- function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
- const hostSourceFile = sourceFilesCache.get(path);
- // No source file on the host
- if (isString(hostSourceFile)) {
- return undefined;
- }
-
- // Create new source file if requested or the versions dont match
- if (!hostSourceFile || shouldCreateNewSourceFile || hostSourceFile.version.toString() !== hostSourceFile.sourceFile.version) {
- const sourceFile = getNewSourceFile();
- if (hostSourceFile) {
- if (shouldCreateNewSourceFile) {
- hostSourceFile.version++;
- }
- if (sourceFile) {
- hostSourceFile.sourceFile = sourceFile;
- sourceFile.version = hostSourceFile.version.toString();
- if (!hostSourceFile.fileWatcher) {
- hostSourceFile.fileWatcher = watchSourceFileForChanges(path);
- }
- }
- else {
- // There is no source file on host any more, close the watch, missing file paths will track it
- hostSourceFile.fileWatcher.close();
- sourceFilesCache.set(path, hostSourceFile.version.toString());
- }
- }
- else {
- let fileWatcher: FileWatcher;
- if (sourceFile) {
- sourceFile.version = "0";
- fileWatcher = watchSourceFileForChanges(path);
- sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher });
- }
- else {
- sourceFilesCache.set(path, "0");
- }
- }
- return sourceFile;
- }
- return hostSourceFile.sourceFile;
-
- function getNewSourceFile() {
- let text: string;
- try {
- performance.mark("beforeIORead");
- text = host.readFile(fileName, compilerOptions.charset);
- performance.mark("afterIORead");
- performance.measure("I/O Read", "beforeIORead", "afterIORead");
- }
- catch (e) {
- if (onError) {
- onError(e.message);
- }
- }
-
- return text !== undefined ? createSourceFile(fileName, text, languageVersion) : undefined;
- }
- }
-
- function removeSourceFile(path: Path) {
- const hostSourceFile = sourceFilesCache.get(path);
- if (hostSourceFile !== undefined) {
- if (!isString(hostSourceFile)) {
- hostSourceFile.fileWatcher.close();
- }
- sourceFilesCache.delete(path);
- }
- }
-
- function getSourceVersion(path: Path): string {
- const hostSourceFile = sourceFilesCache.get(path);
- return !hostSourceFile || isString(hostSourceFile) ? undefined : hostSourceFile.version.toString();
- }
-
- function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) {
- const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.path);
- // If this is the source file thats in the cache and new program doesnt need it,
- // remove the cached entry.
- // Note we arent deleting entry if file became missing in new program or
- // there was version update and new source file was created.
- if (hostSourceFileInfo && !isString(hostSourceFileInfo) && hostSourceFileInfo.sourceFile === oldSourceFile) {
- sourceFilesCache.delete(oldSourceFile.path);
- }
- }
-
- // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch
- // operations (such as saving all modified files in an editor) a chance to complete before we kick
- // off a new compilation.
- function scheduleProgramUpdate() {
- if (!system.setTimeout || !system.clearTimeout) {
- return;
- }
-
- if (timerToUpdateProgram) {
- system.clearTimeout(timerToUpdateProgram);
- }
- timerToUpdateProgram = system.setTimeout(updateProgram, 250);
- }
-
- function scheduleProgramReload() {
- Debug.assert(!!configFileName);
- needsReload = true;
- scheduleProgramUpdate();
- }
-
- function updateProgram() {
- timerToUpdateProgram = undefined;
- reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation), system);
-
- if (needsReload) {
- reloadConfigFile();
- }
- else {
- synchronizeProgram();
- }
- }
-
- function reloadConfigFile() {
- writeLog(`Reloading config file: ${configFileName}`);
- needsReload = false;
-
- const cachedHost = host as CachedSystem;
- cachedHost.clearCache();
- const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost);
- rootFileNames = configParseResult.fileNames;
- compilerOptions = configParseResult.options;
- configFileSpecs = configParseResult.configFileSpecs;
- configFileWildCardDirectories = configParseResult.wildcardDirectories;
-
- synchronizeProgram();
-
- // Update the wild card directory watch
- watchConfigFileWildCardDirectories();
- }
-
- function watchSourceFileForChanges(path: Path) {
- return host.watchFile(path, (fileName, eventKind) => onSourceFileChange(fileName, path, eventKind));
- }
-
- function onSourceFileChange(fileName: string, path: Path, eventKind: FileWatcherEventKind) {
- writeLog(`Source file path : ${path} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`);
-
- updateCachedSystem(fileName, path);
- const hostSourceFile = sourceFilesCache.get(path);
- if (hostSourceFile) {
- // Update the cache
- if (eventKind === FileWatcherEventKind.Deleted) {
- if (!isString(hostSourceFile)) {
- hostSourceFile.fileWatcher.close();
- sourceFilesCache.set(path, (hostSourceFile.version++).toString());
- }
- }
- else {
- // Deleted file created
- if (isString(hostSourceFile)) {
- sourceFilesCache.delete(path);
- }
- else {
- // file changed - just update the version
- hostSourceFile.version++;
- }
- }
- }
-
- // Update the program
- scheduleProgramUpdate();
- }
-
- function updateCachedSystem(fileName: string, path: Path) {
- if (configFileName) {
- const absoluteNormalizedPath = getNormalizedAbsolutePath(fileName, getDirectoryPath(path));
- (host as CachedSystem).addOrDeleteFileOrFolder(normalizePath(absoluteNormalizedPath));
- }
- }
-
- function watchMissingFilePath(missingFilePath: Path) {
- return host.watchFile(missingFilePath, (fileName, eventKind) => onMissingFileChange(fileName, missingFilePath, eventKind));
- }
-
- function closeMissingFilePathWatcher(_missingFilePath: Path, fileWatcher: FileWatcher) {
- fileWatcher.close();
- }
-
- function onMissingFileChange(filename: string, missingFilePath: Path, eventKind: FileWatcherEventKind) {
- writeLog(`Missing file path : ${missingFilePath} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${filename}`);
- if (eventKind === FileWatcherEventKind.Created && missingFilesMap.has(missingFilePath)) {
- closeMissingFilePathWatcher(missingFilePath, missingFilesMap.get(missingFilePath));
- missingFilesMap.delete(missingFilePath);
-
- updateCachedSystem(filename, missingFilePath);
-
- // Delete the entry in the source files cache so that new source file is created
- removeSourceFile(missingFilePath);
-
- // When a missing file is created, we should update the graph.
- scheduleProgramUpdate();
- }
- }
-
- function watchConfigFileWildCardDirectories() {
- const wildcards = createMapFromTemplate(configFileWildCardDirectories);
- watchedWildCardDirectories = updateWatchingWildcardDirectories(
- watchedWildCardDirectories, wildcards,
- watchWildCardDirectory, stopWatchingWildCardDirectory
- );
- }
-
- function watchWildCardDirectory(directory: string, recursive: boolean) {
- return host.watchDirectory(directory, fileName =>
- onFileAddOrRemoveInWatchedDirectory(getNormalizedAbsolutePath(fileName, directory)),
- recursive);
- }
-
- function stopWatchingWildCardDirectory(_directory: string, fileWatcher: FileWatcher, _recursive: boolean, _recursiveChanged: boolean) {
- fileWatcher.close();
- }
-
- function onFileAddOrRemoveInWatchedDirectory(fileName: string) {
- Debug.assert(!!configFileName);
-
- const path = toPath(fileName, currentDirectory, getCanonicalFileName);
-
- // Since the file existance changed, update the sourceFiles cache
- updateCachedSystem(fileName, path);
- removeSourceFile(path);
-
- // If a change was made inside "folder/file", node will trigger the callback twice:
- // one with the fileName being "folder/file", and the other one with "folder".
- // We don't respond to the second one.
- if (fileName && !isSupportedSourceFileName(fileName, compilerOptions)) {
- writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileName}`);
- return;
- }
-
- writeLog(`Project: ${configFileName} Detected file add/remove of supported extension: ${fileName}`);
-
- // Reload is pending, do the reload
- if (!needsReload) {
- const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, host, /*extraFileExtensions*/ []);
- if (!configFileSpecs.filesSpecs) {
- reportDiagnostics([getErrorForNoInputFiles(configFileSpecs, configFileName)], getDefaultFormatDiagnosticsHost(system));
- }
- rootFileNames = result.fileNames;
-
- // Schedule Update the program
- scheduleProgramUpdate();
- }
- }
-
- function onConfigFileChanged(fileName: string, eventKind: FileWatcherEventKind) {
- writeLog(`Config file : ${configFileName} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`);
- scheduleProgramReload();
- }
-
- function writeLog(s: string) {
- const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
- if (hasDiagnostics) {
- host.write(s);
- }
- }
-
- function computeHash(data: string) {
- return system.createHash ? system.createHash(data) : data;
- }
- }
-
- interface CachedSystem extends System {
- addOrDeleteFileOrFolder(fileOrFolder: string): void;
- clearCache(): void;
- }
-
- function createCachedSystem(host: System): CachedSystem {
- const getFileSize = host.getFileSize ? (path: string) => host.getFileSize(path) : undefined;
- const watchFile = host.watchFile ? (path: string, callback: FileWatcherCallback, pollingInterval?: number) => host.watchFile(path, callback, pollingInterval) : undefined;
- const watchDirectory = host.watchDirectory ? (path: string, callback: DirectoryWatcherCallback, recursive?: boolean) => host.watchDirectory(path, callback, recursive) : undefined;
- const getModifiedTime = host.getModifiedTime ? (path: string) => host.getModifiedTime(path) : undefined;
- const createHash = host.createHash ? (data: string) => host.createHash(data) : undefined;
- const getMemoryUsage = host.getMemoryUsage ? () => host.getMemoryUsage() : undefined;
- const realpath = host.realpath ? (path: string) => host.realpath(path) : undefined;
- const tryEnableSourceMapsForHost = host.tryEnableSourceMapsForHost ? () => host.tryEnableSourceMapsForHost() : undefined;
- const setTimeout = host.setTimeout ? (callback: (...args: any[]) => void, ms: number, ...args: any[]) => host.setTimeout(callback, ms, ...args) : undefined;
- const clearTimeout = host.clearTimeout ? (timeoutId: any) => host.clearTimeout(timeoutId) : undefined;
-
- const cachedHost = createCachedHost(host);
- return {
- args: host.args,
- newLine: host.newLine,
- useCaseSensitiveFileNames: host.useCaseSensitiveFileNames,
- write: s => host.write(s),
- readFile: (path, encoding?) => host.readFile(path, encoding),
- getFileSize,
- writeFile: (fileName, data, writeByteOrderMark?) => cachedHost.writeFile(fileName, data, writeByteOrderMark),
- watchFile,
- watchDirectory,
- resolvePath: path => host.resolvePath(path),
- fileExists: fileName => cachedHost.fileExists(fileName),
- directoryExists: dir => cachedHost.directoryExists(dir),
- createDirectory: dir => cachedHost.createDirectory(dir),
- getExecutingFilePath: () => host.getExecutingFilePath(),
- getCurrentDirectory: () => cachedHost.getCurrentDirectory(),
- getDirectories: dir => cachedHost.getDirectories(dir),
- readDirectory: (path, extensions, excludes, includes, depth) => cachedHost.readDirectory(path, extensions, excludes, includes, depth),
- getModifiedTime,
- createHash,
- getMemoryUsage,
- exit: exitCode => host.exit(exitCode),
- realpath,
- getEnvironmentVariable: name => host.getEnvironmentVariable(name),
- tryEnableSourceMapsForHost,
- debugMode: host.debugMode,
- setTimeout,
- clearTimeout,
- addOrDeleteFileOrFolder: fileOrFolder => cachedHost.addOrDeleteFileOrFolder(fileOrFolder),
- clearCache: () => cachedHost.clearCache()
+ function createWatchingSystemHost(reportWatchDiagnostic?: DiagnosticReporter) {
+ const watchingHost = ts.createWatchingSystemHost(/*pretty*/ undefined, sys, parseConfigFile, reportDiagnostic, reportWatchDiagnostic);
+ watchingHost.beforeCompile = enableStatistics;
+ const afterCompile = watchingHost.afterCompile;
+ watchingHost.afterCompile = (host, program, builder) => {
+ afterCompile(host, program, builder);
+ reportStatistics(program);
};
+ return watchingHost;
}
- /* @internal */
- export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: System): ParsedCommandLine {
- let configFileText: string;
- try {
- configFileText = system.readFile(configFileName);
- }
- catch (e) {
- const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message);
- reportWatchDiagnostic(error, system);
- system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
- return;
- }
- if (!configFileText) {
- const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName);
- reportDiagnostics([error], getDefaultFormatDiagnosticsHost(system));
- system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
- return;
- }
-
- const result = parseJsonText(configFileName, configFileText);
- reportDiagnostics(result.parseDiagnostics, getDefaultFormatDiagnosticsHost(system));
-
- const cwd = system.getCurrentDirectory();
- const configParseResult = parseJsonSourceFileConfigFileContent(result, system, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
- reportDiagnostics(configParseResult.errors, getDefaultFormatDiagnosticsHost(system));
-
- return configParseResult;
- }
-
- function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost, system: System, oldProgram?: Program) {
- const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
- let statistics: Statistic[];
- if (hasDiagnostics) {
+ function enableStatistics(compilerOptions: CompilerOptions) {
+ if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) {
performance.enable();
+ }
+ }
+
+ function reportStatistics(program: Program) {
+ let statistics: Statistic[];
+ const compilerOptions = program.getCompilerOptions();
+ if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) {
statistics = [];
- }
-
- const program = createProgram(fileNames, compilerOptions, compilerHost, oldProgram);
- const exitStatus = compileProgram();
-
- if (compilerOptions.listFiles) {
- forEach(program.getSourceFiles(), file => {
- system.write(file.fileName + system.newLine);
- });
- }
-
- if (hasDiagnostics) {
- const memoryUsed = system.getMemoryUsage ? system.getMemoryUsage() : -1;
+ const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
reportCountStatistic("Files", program.getSourceFiles().length);
reportCountStatistic("Lines", countLines(program));
reportCountStatistic("Nodes", program.getNodeCount());
@@ -815,50 +222,6 @@ namespace ts {
performance.disable();
}
- return { program, exitStatus };
-
- function compileProgram(): ExitStatus {
- let diagnostics: Diagnostic[];
-
- // First get and report any syntactic errors.
- diagnostics = program.getSyntacticDiagnostics();
-
- // If we didn't have any syntactic errors, then also try getting the global and
- // semantic errors.
- if (diagnostics.length === 0) {
- diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
-
- if (diagnostics.length === 0) {
- diagnostics = program.getSemanticDiagnostics();
- }
- }
-
- // Emit and report any errors we ran into.
- let emitOutput: EmitResult;
- if (compilerHost.emitWithBuilder) {
- emitOutput = compilerHost.emitWithBuilder(program);
- }
- else {
- // Emit whole program
- emitOutput = program.emit();
- }
- diagnostics = diagnostics.concat(emitOutput.diagnostics);
-
- reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), getDefaultFormatDiagnosticsHost(system));
-
- reportEmittedFiles(emitOutput.emittedFiles, system);
- if (emitOutput.emitSkipped && diagnostics.length > 0) {
- // If the emitter didn't emit anything, then pass that value along.
- return ExitStatus.DiagnosticsPresent_OutputsSkipped;
- }
- else if (diagnostics.length > 0) {
- // The emitter emitted something, inform the caller if that happened in the presence
- // of diagnostics or not.
- return ExitStatus.DiagnosticsPresent_OutputsGenerated;
- }
- return ExitStatus.Success;
- }
-
function reportStatistics() {
let nameSize = 0;
let valueSize = 0;
@@ -873,7 +236,7 @@ namespace ts {
}
for (const { name, value } of statistics) {
- system.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + system.newLine);
+ sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine);
}
}
@@ -1012,11 +375,11 @@ namespace ts {
const currentDirectory = sys.getCurrentDirectory();
const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json"));
if (sys.fileExists(file)) {
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file));
}
else {
sys.writeFile(file, generateTSConfig(options, fileNames, sys.newLine));
- reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file), /* host */ undefined);
+ reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file));
}
return;
diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json
index 360819f2b36..fd92a6008cd 100644
--- a/src/compiler/tsconfig.json
+++ b/src/compiler/tsconfig.json
@@ -37,6 +37,7 @@
"emitter.ts",
"program.ts",
"builder.ts",
+ "watchedProgram.ts",
"commandLineParser.ts",
"tscLib.ts",
"tsc.ts",
diff --git a/src/compiler/watchedProgram.ts b/src/compiler/watchedProgram.ts
new file mode 100644
index 00000000000..22625282a8d
--- /dev/null
+++ b/src/compiler/watchedProgram.ts
@@ -0,0 +1,673 @@
+///
+///
+
+namespace ts {
+ export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
+ export type DiagnosticWorker = (diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System) => void;
+ export type ParseConfigFile = (configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter) => ParsedCommandLine;
+ export interface WatchingSystemHost {
+ // FS system to use
+ system: System;
+
+ // parse config file
+ parseConfigFile: ParseConfigFile;
+
+ // Reporting errors
+ reportDiagnostic: DiagnosticReporter;
+ reportWatchDiagnostic: DiagnosticReporter;
+
+ // Callbacks to do custom action before creating program and after creating program
+ beforeCompile(compilerOptions: CompilerOptions): void;
+ afterCompile(host: System, program: Program, builder: Builder): void;
+ }
+
+ const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? {
+ getCurrentDirectory: () => sys.getCurrentDirectory(),
+ getNewLine: () => sys.newLine,
+ getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames)
+ } : undefined;
+
+ export function createDiagnosticReporter(system = sys, worker = reportDiagnosticSimply, formatDiagnosticsHost?: FormatDiagnosticsHost): DiagnosticReporter {
+ return diagnostic => worker(diagnostic, getFormatDiagnosticsHost(), system);
+
+ function getFormatDiagnosticsHost() {
+ return formatDiagnosticsHost || (formatDiagnosticsHost = system === sys ? defaultFormatDiagnosticsHost : {
+ getCurrentDirectory: () => system.getCurrentDirectory(),
+ getNewLine: () => system.newLine,
+ getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames),
+ });
+ }
+ }
+
+ export function createWatchDiagnosticReporter(system = sys): DiagnosticReporter {
+ return diagnostic => {
+ let output = new Date().toLocaleTimeString() + " - ";
+ output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine + system.newLine}`;
+ system.write(output);
+ };
+ }
+
+ export function reportDiagnostics(diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter): void {
+ for (const diagnostic of diagnostics) {
+ reportDiagnostic(diagnostic);
+ }
+ }
+
+ export function reportDiagnosticSimply(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void {
+ system.write(ts.formatDiagnostic(diagnostic, host));
+ }
+
+ export function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void {
+ system.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + host.getNewLine());
+ }
+
+ export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter): ParsedCommandLine {
+ let configFileText: string;
+ try {
+ configFileText = system.readFile(configFileName);
+ }
+ catch (e) {
+ const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message);
+ reportWatchDiagnostic(error);
+ system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
+ return;
+ }
+ if (!configFileText) {
+ const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName);
+ reportDiagnostics([error], reportDiagnostic);
+ system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
+ return;
+ }
+
+ const result = parseJsonText(configFileName, configFileText);
+ reportDiagnostics(result.parseDiagnostics, reportDiagnostic);
+
+ const cwd = system.getCurrentDirectory();
+ const configParseResult = parseJsonSourceFileConfigFileContent(result, system, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
+ reportDiagnostics(configParseResult.errors, reportDiagnostic);
+
+ return configParseResult;
+ }
+
+ function reportEmittedFiles(files: string[], system: System): void {
+ if (!files || files.length === 0) {
+ return;
+ }
+ const currentDir = system.getCurrentDirectory();
+ for (const file of files) {
+ const filepath = getNormalizedAbsolutePath(file, currentDir);
+ system.write(`TSFILE: ${filepath}${system.newLine}`);
+ }
+ }
+
+ export function compileProgram(system: System, program: Program, emitProgram: () => EmitResult,
+ reportDiagnostic: DiagnosticReporter): ExitStatus {
+ let diagnostics: Diagnostic[];
+
+ // First get and report any syntactic errors.
+ diagnostics = program.getSyntacticDiagnostics();
+
+ // If we didn't have any syntactic errors, then also try getting the global and
+ // semantic errors.
+ if (diagnostics.length === 0) {
+ diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
+
+ if (diagnostics.length === 0) {
+ diagnostics = program.getSemanticDiagnostics();
+ }
+ }
+
+ // Emit and report any errors we ran into.
+ const emitOutput = emitProgram();
+ diagnostics = diagnostics.concat(emitOutput.diagnostics);
+
+ reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), reportDiagnostic);
+ reportEmittedFiles(emitOutput.emittedFiles, system);
+
+ if (program.getCompilerOptions().listFiles) {
+ forEach(program.getSourceFiles(), file => {
+ system.write(file.fileName + system.newLine);
+ });
+ }
+
+ if (emitOutput.emitSkipped && diagnostics.length > 0) {
+ // If the emitter didn't emit anything, then pass that value along.
+ return ExitStatus.DiagnosticsPresent_OutputsSkipped;
+ }
+ else if (diagnostics.length > 0) {
+ // The emitter emitted something, inform the caller if that happened in the presence
+ // of diagnostics or not.
+ return ExitStatus.DiagnosticsPresent_OutputsGenerated;
+ }
+ return ExitStatus.Success;
+ }
+
+ function emitWatchedProgram(host: System, program: Program, builder: Builder) {
+ const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined;
+ let sourceMaps: SourceMapData[];
+ let emitSkipped: boolean;
+ let diagnostics: Diagnostic[];
+
+ const result = builder.emitChangedFiles(program);
+ switch (result.length) {
+ case 0:
+ emitSkipped = true;
+ break;
+ case 1:
+ const emitOutput = result[0];
+ ({ diagnostics, sourceMaps, emitSkipped } = emitOutput);
+ writeOutputFiles(emitOutput.outputFiles);
+ break;
+ default:
+ for (const emitOutput of result) {
+ if (emitOutput.emitSkipped) {
+ emitSkipped = true;
+ }
+ diagnostics = concatenate(diagnostics, emitOutput.diagnostics);
+ sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
+ writeOutputFiles(emitOutput.outputFiles);
+ }
+ }
+
+ return { emitSkipped, diagnostics: diagnostics || [], emittedFiles, sourceMaps };
+
+
+ function ensureDirectoriesExist(directoryPath: string) {
+ if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) {
+ const parentDirectory = getDirectoryPath(directoryPath);
+ ensureDirectoriesExist(parentDirectory);
+ host.createDirectory(directoryPath);
+ }
+ }
+
+ function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
+ try {
+ performance.mark("beforeIOWrite");
+ ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
+
+ host.writeFile(fileName, data, writeByteOrderMark);
+
+ performance.mark("afterIOWrite");
+ performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
+ }
+ catch (e) {
+ return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e);
+ }
+ }
+
+ function writeOutputFiles(outputFiles: OutputFile[]) {
+ if (outputFiles) {
+ for (const outputFile of outputFiles) {
+ const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark);
+ if (error) {
+ diagnostics.push(error);
+ }
+ if (emittedFiles) {
+ emittedFiles.push(outputFile.name);
+ }
+ }
+ }
+ }
+ }
+
+ export function createWatchingSystemHost(pretty?: DiagnosticStyle, system = sys,
+ parseConfigFile?: ParseConfigFile, reportDiagnostic?: DiagnosticReporter,
+ reportWatchDiagnostic?: DiagnosticReporter
+ ): WatchingSystemHost {
+ reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system, pretty ? reportDiagnosticWithColorAndContext : reportDiagnosticSimply);
+ reportWatchDiagnostic = reportWatchDiagnostic || createWatchDiagnosticReporter(system);
+ parseConfigFile = parseConfigFile || ts.parseConfigFile;
+ return {
+ system,
+ parseConfigFile,
+ reportDiagnostic,
+ reportWatchDiagnostic,
+ beforeCompile: noop,
+ afterCompile: compileWatchedProgram,
+ };
+
+ function compileWatchedProgram(host: System, program: Program, builder: Builder) {
+ return compileProgram(system, program, () => emitWatchedProgram(host, program, builder), reportDiagnostic);
+ }
+ }
+
+ export function createWatchModeWithConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions = {}, watchingHost?: WatchingSystemHost) {
+ return createWatchMode(configParseResult.fileNames, configParseResult.options, watchingHost, configParseResult.options.configFilePath, configParseResult.configFileSpecs, configParseResult.wildcardDirectories, optionsToExtend);
+ }
+
+ export function createWatchModeWithoutConfigFile(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost) {
+ return createWatchMode(rootFileNames, compilerOptions, watchingHost);
+ }
+
+ interface HostFileInfo {
+ version: number;
+ sourceFile: SourceFile;
+ fileWatcher: FileWatcher;
+ }
+
+ function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike, optionsToExtendForConfigFile?: CompilerOptions) {
+ let program: Program;
+ let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
+ let missingFilesMap: Map; // Map of file watchers for the missing files
+ let configFileWatcher: FileWatcher; // watcher for the config file
+ let watchedWildCardDirectories: Map; // map of watchers for the wild card directories in the config file
+ let timerToUpdateProgram: any; // timer callback to recompile the program
+
+ const sourceFilesCache = createMap(); // Cache that stores the source file and version info
+ watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
+ const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
+
+ let host: System;
+ if (configFileName) {
+ host = createCachedSystem(system);
+ configFileWatcher = system.watchFile(configFileName, onConfigFileChanged);
+ }
+ else {
+ host = system;
+ }
+ const currentDirectory = host.getCurrentDirectory();
+ const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
+
+ // There is no extra check needed since we can just rely on the program to decide emit
+ const builder = createBuilder(getCanonicalFileName, getFileEmitOutput, computeHash, _sourceFile => true);
+
+ synchronizeProgram();
+
+ // Update the wild card directory watch
+ watchConfigFileWildCardDirectories();
+
+ return () => program;
+
+ function synchronizeProgram() {
+ writeLog(`Synchronizing program`);
+
+ if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists)) {
+ return;
+ }
+
+ // Create the compiler host
+ const compilerHost = createWatchedCompilerHost(compilerOptions);
+ beforeCompile(compilerOptions);
+
+ // Compile the program
+ program = createProgram(rootFileNames, compilerOptions, compilerHost, program);
+ builder.onProgramUpdateGraph(program);
+
+ // Update watches
+ missingFilesMap = updateMissingFilePathsWatch(program, missingFilesMap, watchMissingFilePath, closeMissingFilePathWatcher);
+
+ afterCompile(host, program, builder);
+ reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
+ }
+
+ function createWatchedCompilerHost(options: CompilerOptions): CompilerHost {
+ const newLine = getNewLineCharacter(options, system);
+ const realpath = host.realpath && ((path: string) => host.realpath(path));
+
+ return {
+ getSourceFile: getVersionedSourceFile,
+ getSourceFileByPath: getVersionedSourceFileByPath,
+ getDefaultLibLocation,
+ getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
+ writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { },
+ getCurrentDirectory: memoize(() => host.getCurrentDirectory()),
+ useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames,
+ getCanonicalFileName,
+ getNewLine: () => newLine,
+ fileExists,
+ readFile: fileName => host.readFile(fileName),
+ trace: (s: string) => host.write(s + newLine),
+ directoryExists: directoryName => host.directoryExists(directoryName),
+ getEnvironmentVariable: name => host.getEnvironmentVariable ? host.getEnvironmentVariable(name) : "",
+ getDirectories: (path: string) => host.getDirectories(path),
+ realpath,
+ onReleaseOldSourceFile,
+ };
+
+ // TODO: cache module resolution
+ // if (host.resolveModuleNames) {
+ // compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile);
+ // }
+ // if (host.resolveTypeReferenceDirectives) {
+ // compilerHost.resolveTypeReferenceDirectives = (typeReferenceDirectiveNames, containingFile) => {
+ // return host.resolveTypeReferenceDirectives(typeReferenceDirectiveNames, containingFile);
+ // };
+ // }
+ }
+
+ function fileExists(fileName: string) {
+ const path = toPath(fileName, currentDirectory, getCanonicalFileName);
+ const hostSourceFileInfo = sourceFilesCache.get(path);
+ if (hostSourceFileInfo !== undefined) {
+ return !isString(hostSourceFileInfo);
+ }
+
+ return host.fileExists(fileName);
+ }
+
+ function getDefaultLibLocation(): string {
+ return getDirectoryPath(normalizePath(host.getExecutingFilePath()));
+ }
+
+ function getVersionedSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
+ return getVersionedSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), languageVersion, onError, shouldCreateNewSourceFile);
+ }
+
+ function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
+ const hostSourceFile = sourceFilesCache.get(path);
+ // No source file on the host
+ if (isString(hostSourceFile)) {
+ return undefined;
+ }
+
+ // Create new source file if requested or the versions dont match
+ if (!hostSourceFile || shouldCreateNewSourceFile || hostSourceFile.version.toString() !== hostSourceFile.sourceFile.version) {
+ const sourceFile = getNewSourceFile();
+ if (hostSourceFile) {
+ if (shouldCreateNewSourceFile) {
+ hostSourceFile.version++;
+ }
+ if (sourceFile) {
+ hostSourceFile.sourceFile = sourceFile;
+ sourceFile.version = hostSourceFile.version.toString();
+ if (!hostSourceFile.fileWatcher) {
+ hostSourceFile.fileWatcher = watchSourceFileForChanges(path);
+ }
+ }
+ else {
+ // There is no source file on host any more, close the watch, missing file paths will track it
+ hostSourceFile.fileWatcher.close();
+ sourceFilesCache.set(path, hostSourceFile.version.toString());
+ }
+ }
+ else {
+ let fileWatcher: FileWatcher;
+ if (sourceFile) {
+ sourceFile.version = "0";
+ fileWatcher = watchSourceFileForChanges(path);
+ sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher });
+ }
+ else {
+ sourceFilesCache.set(path, "0");
+ }
+ }
+ return sourceFile;
+ }
+ return hostSourceFile.sourceFile;
+
+ function getNewSourceFile() {
+ let text: string;
+ try {
+ performance.mark("beforeIORead");
+ text = host.readFile(fileName, compilerOptions.charset);
+ performance.mark("afterIORead");
+ performance.measure("I/O Read", "beforeIORead", "afterIORead");
+ }
+ catch (e) {
+ if (onError) {
+ onError(e.message);
+ }
+ }
+
+ return text !== undefined ? createSourceFile(fileName, text, languageVersion) : undefined;
+ }
+ }
+
+ function removeSourceFile(path: Path) {
+ const hostSourceFile = sourceFilesCache.get(path);
+ if (hostSourceFile !== undefined) {
+ if (!isString(hostSourceFile)) {
+ hostSourceFile.fileWatcher.close();
+ }
+ sourceFilesCache.delete(path);
+ }
+ }
+
+ function getSourceVersion(path: Path): string {
+ const hostSourceFile = sourceFilesCache.get(path);
+ return !hostSourceFile || isString(hostSourceFile) ? undefined : hostSourceFile.version.toString();
+ }
+
+ function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) {
+ const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.path);
+ // If this is the source file thats in the cache and new program doesnt need it,
+ // remove the cached entry.
+ // Note we arent deleting entry if file became missing in new program or
+ // there was version update and new source file was created.
+ if (hostSourceFileInfo && !isString(hostSourceFileInfo) && hostSourceFileInfo.sourceFile === oldSourceFile) {
+ sourceFilesCache.delete(oldSourceFile.path);
+ }
+ }
+
+ // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch
+ // operations (such as saving all modified files in an editor) a chance to complete before we kick
+ // off a new compilation.
+ function scheduleProgramUpdate() {
+ if (!system.setTimeout || !system.clearTimeout) {
+ return;
+ }
+
+ if (timerToUpdateProgram) {
+ system.clearTimeout(timerToUpdateProgram);
+ }
+ timerToUpdateProgram = system.setTimeout(updateProgram, 250);
+ }
+
+ function scheduleProgramReload() {
+ Debug.assert(!!configFileName);
+ needsReload = true;
+ scheduleProgramUpdate();
+ }
+
+ function updateProgram() {
+ timerToUpdateProgram = undefined;
+ reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
+
+ if (needsReload) {
+ reloadConfigFile();
+ }
+ else {
+ synchronizeProgram();
+ }
+ }
+
+ function reloadConfigFile() {
+ writeLog(`Reloading config file: ${configFileName}`);
+ needsReload = false;
+
+ const cachedHost = host as CachedSystem;
+ cachedHost.clearCache();
+ const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic);
+ rootFileNames = configParseResult.fileNames;
+ compilerOptions = configParseResult.options;
+ configFileSpecs = configParseResult.configFileSpecs;
+ configFileWildCardDirectories = configParseResult.wildcardDirectories;
+
+ synchronizeProgram();
+
+ // Update the wild card directory watch
+ watchConfigFileWildCardDirectories();
+ }
+
+ function watchSourceFileForChanges(path: Path) {
+ return host.watchFile(path, (fileName, eventKind) => onSourceFileChange(fileName, path, eventKind));
+ }
+
+ function onSourceFileChange(fileName: string, path: Path, eventKind: FileWatcherEventKind) {
+ writeLog(`Source file path : ${path} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`);
+
+ updateCachedSystem(fileName, path);
+ const hostSourceFile = sourceFilesCache.get(path);
+ if (hostSourceFile) {
+ // Update the cache
+ if (eventKind === FileWatcherEventKind.Deleted) {
+ if (!isString(hostSourceFile)) {
+ hostSourceFile.fileWatcher.close();
+ sourceFilesCache.set(path, (hostSourceFile.version++).toString());
+ }
+ }
+ else {
+ // Deleted file created
+ if (isString(hostSourceFile)) {
+ sourceFilesCache.delete(path);
+ }
+ else {
+ // file changed - just update the version
+ hostSourceFile.version++;
+ }
+ }
+ }
+
+ // Update the program
+ scheduleProgramUpdate();
+ }
+
+ function updateCachedSystem(fileName: string, path: Path) {
+ if (configFileName) {
+ const absoluteNormalizedPath = getNormalizedAbsolutePath(fileName, getDirectoryPath(path));
+ (host as CachedSystem).addOrDeleteFileOrFolder(normalizePath(absoluteNormalizedPath));
+ }
+ }
+
+ function watchMissingFilePath(missingFilePath: Path) {
+ return host.watchFile(missingFilePath, (fileName, eventKind) => onMissingFileChange(fileName, missingFilePath, eventKind));
+ }
+
+ function closeMissingFilePathWatcher(_missingFilePath: Path, fileWatcher: FileWatcher) {
+ fileWatcher.close();
+ }
+
+ function onMissingFileChange(filename: string, missingFilePath: Path, eventKind: FileWatcherEventKind) {
+ writeLog(`Missing file path : ${missingFilePath} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${filename}`);
+ if (eventKind === FileWatcherEventKind.Created && missingFilesMap.has(missingFilePath)) {
+ closeMissingFilePathWatcher(missingFilePath, missingFilesMap.get(missingFilePath));
+ missingFilesMap.delete(missingFilePath);
+
+ updateCachedSystem(filename, missingFilePath);
+
+ // Delete the entry in the source files cache so that new source file is created
+ removeSourceFile(missingFilePath);
+
+ // When a missing file is created, we should update the graph.
+ scheduleProgramUpdate();
+ }
+ }
+
+ function watchConfigFileWildCardDirectories() {
+ const wildcards = createMapFromTemplate(configFileWildCardDirectories);
+ watchedWildCardDirectories = updateWatchingWildcardDirectories(
+ watchedWildCardDirectories, wildcards,
+ watchWildCardDirectory, stopWatchingWildCardDirectory
+ );
+ }
+
+ function watchWildCardDirectory(directory: string, recursive: boolean) {
+ return host.watchDirectory(directory, fileName =>
+ onFileAddOrRemoveInWatchedDirectory(getNormalizedAbsolutePath(fileName, directory)),
+ recursive);
+ }
+
+ function stopWatchingWildCardDirectory(_directory: string, fileWatcher: FileWatcher, _recursive: boolean, _recursiveChanged: boolean) {
+ fileWatcher.close();
+ }
+
+ function onFileAddOrRemoveInWatchedDirectory(fileName: string) {
+ Debug.assert(!!configFileName);
+
+ const path = toPath(fileName, currentDirectory, getCanonicalFileName);
+
+ // Since the file existance changed, update the sourceFiles cache
+ updateCachedSystem(fileName, path);
+ removeSourceFile(path);
+
+ // If a change was made inside "folder/file", node will trigger the callback twice:
+ // one with the fileName being "folder/file", and the other one with "folder".
+ // We don't respond to the second one.
+ if (fileName && !isSupportedSourceFileName(fileName, compilerOptions)) {
+ writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileName}`);
+ return;
+ }
+
+ writeLog(`Project: ${configFileName} Detected file add/remove of supported extension: ${fileName}`);
+
+ // Reload is pending, do the reload
+ if (!needsReload) {
+ const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, host, /*extraFileExtension*/ []);
+ if (!configFileSpecs.filesSpecs) {
+ reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
+ }
+ rootFileNames = result.fileNames;
+
+ // Schedule Update the program
+ scheduleProgramUpdate();
+ }
+ }
+
+ function onConfigFileChanged(fileName: string, eventKind: FileWatcherEventKind) {
+ writeLog(`Config file : ${configFileName} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`);
+ scheduleProgramReload();
+ }
+
+ function writeLog(s: string) {
+ const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
+ if (hasDiagnostics) {
+ host.write(s);
+ }
+ }
+
+ function computeHash(data: string) {
+ return system.createHash ? system.createHash(data) : data;
+ }
+ }
+
+ interface CachedSystem extends System {
+ addOrDeleteFileOrFolder(fileOrFolder: string): void;
+ clearCache(): void;
+ }
+
+ function createCachedSystem(host: System): CachedSystem {
+ const getFileSize = host.getFileSize ? (path: string) => host.getFileSize(path) : undefined;
+ const watchFile = host.watchFile ? (path: string, callback: FileWatcherCallback, pollingInterval?: number) => host.watchFile(path, callback, pollingInterval) : undefined;
+ const watchDirectory = host.watchDirectory ? (path: string, callback: DirectoryWatcherCallback, recursive?: boolean) => host.watchDirectory(path, callback, recursive) : undefined;
+ const getModifiedTime = host.getModifiedTime ? (path: string) => host.getModifiedTime(path) : undefined;
+ const createHash = host.createHash ? (data: string) => host.createHash(data) : undefined;
+ const getMemoryUsage = host.getMemoryUsage ? () => host.getMemoryUsage() : undefined;
+ const realpath = host.realpath ? (path: string) => host.realpath(path) : undefined;
+ const tryEnableSourceMapsForHost = host.tryEnableSourceMapsForHost ? () => host.tryEnableSourceMapsForHost() : undefined;
+ const setTimeout = host.setTimeout ? (callback: (...args: any[]) => void, ms: number, ...args: any[]) => host.setTimeout(callback, ms, ...args) : undefined;
+ const clearTimeout = host.clearTimeout ? (timeoutId: any) => host.clearTimeout(timeoutId) : undefined;
+
+ const cachedHost = createCachedHost(host);
+ return {
+ args: host.args,
+ newLine: host.newLine,
+ useCaseSensitiveFileNames: host.useCaseSensitiveFileNames,
+ write: s => host.write(s),
+ readFile: (path, encoding?) => host.readFile(path, encoding),
+ getFileSize,
+ writeFile: (fileName, data, writeByteOrderMark?) => cachedHost.writeFile(fileName, data, writeByteOrderMark),
+ watchFile,
+ watchDirectory,
+ resolvePath: path => host.resolvePath(path),
+ fileExists: fileName => cachedHost.fileExists(fileName),
+ directoryExists: dir => cachedHost.directoryExists(dir),
+ createDirectory: dir => cachedHost.createDirectory(dir),
+ getExecutingFilePath: () => host.getExecutingFilePath(),
+ getCurrentDirectory: () => cachedHost.getCurrentDirectory(),
+ getDirectories: dir => cachedHost.getDirectories(dir),
+ readDirectory: (path, extensions, excludes, includes, depth) => cachedHost.readDirectory(path, extensions, excludes, includes, depth),
+ getModifiedTime,
+ createHash,
+ getMemoryUsage,
+ exit: exitCode => host.exit(exitCode),
+ realpath,
+ getEnvironmentVariable: name => host.getEnvironmentVariable(name),
+ tryEnableSourceMapsForHost,
+ debugMode: host.debugMode,
+ setTimeout,
+ clearTimeout,
+ addOrDeleteFileOrFolder: fileOrFolder => cachedHost.addOrDeleteFileOrFolder(fileOrFolder),
+ clearCache: () => cachedHost.clearCache()
+ };
+ }
+}
diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts
index a55cfb133d7..608fc30c6b9 100644
--- a/src/harness/unittests/tscWatchMode.ts
+++ b/src/harness/unittests/tscWatchMode.ts
@@ -26,9 +26,23 @@ namespace ts.tscWatch {
checkFileNames(`Program rootFileNames`, program.getRootFileNames(), expectedFiles);
}
- export function createWatchWithConfig(configFilePath: string, host: WatchedSystem) {
- const configFileResult = parseConfigFile(configFilePath, {}, host);
- return createWatchModeWithConfigFile(configFileResult, {}, host);
+ function createWatchingSystemHost(system: WatchedSystem) {
+ return ts.createWatchingSystemHost(/*pretty*/ undefined, system);
+ }
+
+ function parseConfigFile(configFileName: string, watchingSystemHost: WatchingSystemHost) {
+ return ts.parseConfigFile(configFileName, {}, watchingSystemHost.system, watchingSystemHost.reportDiagnostic, watchingSystemHost.reportWatchDiagnostic);
+ }
+
+ function createWatchModeWithConfigFile(configFilePath: string, host: WatchedSystem) {
+ const watchingSystemHost = createWatchingSystemHost(host);
+ const configFileResult = parseConfigFile(configFilePath, watchingSystemHost);
+ return ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost);
+ }
+
+ function createWatchModeWithoutConfigFile(fileNames: string[], host: WatchedSystem, options: CompilerOptions = {}) {
+ const watchingSystemHost = createWatchingSystemHost(host);
+ return ts.createWatchModeWithoutConfigFile(fileNames, options, watchingSystemHost);
}
function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) {
@@ -94,7 +108,7 @@ namespace ts.tscWatch {
content: `export let x: number`
};
const host = createWatchedSystem([appFile, moduleFile, libFile]);
- const watch = createWatchModeWithoutConfigFile([appFile.path], {}, host);
+ const watch = createWatchModeWithoutConfigFile([appFile.path], host);
checkProgramActualFiles(watch(), [appFile.path, libFile.path, moduleFile.path]);
@@ -119,7 +133,7 @@ namespace ts.tscWatch {
const host = createWatchedSystem([f1, config], { useCaseSensitiveFileNames: false });
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
- const watch = createWatchWithConfig(upperCaseConfigFilePath, host);
+ const watch = createWatchModeWithConfigFile(upperCaseConfigFilePath, host);
checkProgramActualFiles(watch(), [f1.path]);
});
@@ -148,10 +162,11 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([configFile, libFile, file1, file2, file3]);
- const configFileResult = parseConfigFile(configFile.path, {}, host);
+ const watchingSystemHost = createWatchingSystemHost(host);
+ const configFileResult = parseConfigFile(configFile.path, watchingSystemHost);
assert.equal(configFileResult.errors.length, 0, `expect no errors in config file, got ${JSON.stringify(configFileResult.errors)}`);
- const watch = createWatchModeWithConfigFile(configFileResult, {}, host);
+ const watch = ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost);
checkProgramActualFiles(watch(), [file1.path, libFile.path, file2.path]);
checkProgramRootFiles(watch(), [file1.path, file2.path]);
@@ -169,7 +184,7 @@ namespace ts.tscWatch {
content: `{}`
};
const host = createWatchedSystem([commonFile1, libFile, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkWatchedDirectories(host, ["/a/b"], /*recursive*/ true);
checkProgramRootFiles(watch(), [commonFile1.path]);
@@ -192,7 +207,7 @@ namespace ts.tscWatch {
}`
};
const host = createWatchedSystem([commonFile1, commonFile2, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
const commonFile3 = "/a/b/commonFile3.ts";
checkProgramRootFiles(watch(), [commonFile1.path, commonFile3]);
@@ -205,7 +220,7 @@ namespace ts.tscWatch {
content: `{}`
};
const host = createWatchedSystem([commonFile1, commonFile2, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]);
// delete commonFile2
@@ -226,7 +241,7 @@ namespace ts.tscWatch {
let x = y`
};
const host = createWatchedSystem([file1, libFile]);
- const watch = createWatchModeWithoutConfigFile([file1.path], {}, host);
+ const watch = createWatchModeWithoutConfigFile([file1.path], host);
checkProgramRootFiles(watch(), [file1.path]);
checkProgramActualFiles(watch(), [file1.path, libFile.path]);
@@ -254,7 +269,7 @@ namespace ts.tscWatch {
};
const files = [commonFile1, commonFile2, configFile];
const host = createWatchedSystem(files);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]);
configFile.content = `{
@@ -281,7 +296,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([commonFile1, commonFile2, excludedFile1, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]);
});
@@ -309,7 +324,7 @@ namespace ts.tscWatch {
};
const files = [file1, nodeModuleFile, classicModuleFile, configFile];
const host = createWatchedSystem(files);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramRootFiles(watch(), [file1.path]);
checkProgramActualFiles(watch(), [file1.path, nodeModuleFile.path]);
@@ -337,7 +352,7 @@ namespace ts.tscWatch {
}`
};
const host = createWatchedSystem([commonFile1, commonFile2, libFile, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]);
});
@@ -355,7 +370,7 @@ namespace ts.tscWatch {
content: `export let y = 1;`
};
const host = createWatchedSystem([file1, file2, file3]);
- const watch = createWatchModeWithoutConfigFile([file1.path], {}, host);
+ const watch = createWatchModeWithoutConfigFile([file1.path], host);
checkProgramRootFiles(watch(), [file1.path]);
checkProgramActualFiles(watch(), [file1.path, file2.path]);
@@ -384,7 +399,7 @@ namespace ts.tscWatch {
content: `export let y = 1;`
};
const host = createWatchedSystem([file1, file2, file3]);
- const watch = createWatchModeWithoutConfigFile([file1.path], {}, host);
+ const watch = createWatchModeWithoutConfigFile([file1.path], host);
checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]);
host.reloadFS([file1, file3]);
@@ -407,7 +422,7 @@ namespace ts.tscWatch {
content: `export let y = 1;`
};
const host = createWatchedSystem([file1, file2, file3]);
- const watch = createWatchModeWithoutConfigFile([file1.path, file3.path], {}, host);
+ const watch = createWatchModeWithoutConfigFile([file1.path, file3.path], host);
checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]);
host.reloadFS([file1, file3]);
@@ -435,7 +450,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file1, file2, file3, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramRootFiles(watch(), [file2.path, file3.path]);
checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]);
@@ -457,10 +472,10 @@ namespace ts.tscWatch {
content: "export let y = 1;"
};
const host = createWatchedSystem([file1, file2, file3]);
- const watch = createWatchModeWithoutConfigFile([file2.path, file3.path], {}, host);
+ const watch = createWatchModeWithoutConfigFile([file2.path, file3.path], host);
checkProgramActualFiles(watch(), [file2.path, file3.path]);
- const watch2 = createWatchModeWithoutConfigFile([file1.path], {}, host);
+ const watch2 = createWatchModeWithoutConfigFile([file1.path], host);
checkProgramActualFiles(watch2(), [file1.path, file2.path, file3.path]);
// Previous program shouldnt be updated
@@ -483,7 +498,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file1, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), [file1.path]);
host.reloadFS([file1, file2, configFile]);
@@ -508,7 +523,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file1, file2, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), [file1.path]);
@@ -538,7 +553,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file1, file2, configFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), [file1.path, file2.path]);
const modifiedConfigFile = {
@@ -566,7 +581,7 @@ namespace ts.tscWatch {
content: JSON.stringify({ compilerOptions: {} })
};
const host = createWatchedSystem([file1, file2, config]);
- const watch = createWatchWithConfig(config.path, host);
+ const watch = createWatchModeWithConfigFile(config.path, host);
checkProgramActualFiles(watch(), [file1.path, file2.path]);
@@ -588,7 +603,7 @@ namespace ts.tscWatch {
content: "{"
};
const host = createWatchedSystem([file1, corruptedConfig]);
- const watch = createWatchWithConfig(corruptedConfig.path, host);
+ const watch = createWatchModeWithConfigFile(corruptedConfig.path, host);
checkProgramActualFiles(watch(), [file1.path]);
});
@@ -638,7 +653,7 @@ namespace ts.tscWatch {
})
};
const host = createWatchedSystem([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" });
- const watch = createWatchWithConfig(config1.path, host);
+ const watch = createWatchModeWithConfigFile(config1.path, host);
checkProgramActualFiles(watch(), [libES5.path, app.path]);
@@ -663,7 +678,7 @@ namespace ts.tscWatch {
})
};
const host = createWatchedSystem([f, config]);
- const watch = createWatchWithConfig(config.path, host);
+ const watch = createWatchModeWithConfigFile(config.path, host);
checkProgramActualFiles(watch(), [f.path]);
});
@@ -677,7 +692,7 @@ namespace ts.tscWatch {
content: "import * as T from './moduleFile'; T.bar();"
};
const host = createWatchedSystem([moduleFile, file1, libFile]);
- createWatchModeWithoutConfigFile([file1.path], {}, host);
+ createWatchModeWithoutConfigFile([file1.path], host);
const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n";
checkOutputDoesNotContain(host, [error]);
@@ -709,7 +724,7 @@ namespace ts.tscWatch {
content: `{}`
};
const host = createWatchedSystem([moduleFile, file1, configFile, libFile]);
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
const error = `error TS6053: File '${moduleFile.path}' not found.${host.newLine}`;
checkOutputDoesNotContain(host, [error]);
@@ -745,7 +760,7 @@ namespace ts.tscWatch {
path: "/a/c"
};
const host = createWatchedSystem([f1, config, node, cwd], { currentDirectory: cwd.path });
- const watch = createWatchWithConfig(config.path, host);
+ const watch = createWatchModeWithConfigFile(config.path, host);
checkProgramActualFiles(watch(), [f1.path, node.path]);
});
@@ -760,7 +775,7 @@ namespace ts.tscWatch {
content: "import * as T from './moduleFile'; T.bar();"
};
const host = createWatchedSystem([file1, libFile]);
- createWatchModeWithoutConfigFile([file1.path], {}, host);
+ createWatchModeWithoutConfigFile([file1.path], host);
const error = `a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.${host.newLine}`;
checkOutputContains(host, [error]);
@@ -787,7 +802,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file, configFile, libFile]);
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
checkOutputContains(host, [
`a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`,
`a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}`
@@ -807,7 +822,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file, configFile, libFile]);
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
checkOutputDoesNotContain(host, [
`a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`,
`a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}`
@@ -827,7 +842,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file, configFile, libFile]);
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
const error = `a/b/tsconfig.json(3,25): error TS5023: Unknown compiler option 'haha'.${host.newLine}`;
checkOutputDoesNotContain(host, [error]);
@@ -863,7 +878,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file1, configFile, libFile]);
- const watch = createWatchWithConfig(configFile.path, host);
+ const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), [libFile.path]);
});
@@ -889,7 +904,7 @@ namespace ts.tscWatch {
content: `export const x: number`
};
const host = createWatchedSystem([f, config, t1, t2], { currentDirectory: getDirectoryPath(f.path) });
- const watch = createWatchWithConfig(config.path, host);
+ const watch = createWatchModeWithConfigFile(config.path, host);
checkProgramActualFiles(watch(), [t1.path, t2.path]);
});
@@ -900,7 +915,7 @@ namespace ts.tscWatch {
content: "let x = 1"
};
const host = createWatchedSystem([f, libFile]);
- const watch = createWatchModeWithoutConfigFile([f.path], { allowNonTsExtensions: true }, host);
+ const watch = createWatchModeWithoutConfigFile([f.path], host, { allowNonTsExtensions: true });
checkProgramActualFiles(watch(), [f.path, libFile.path]);
});
@@ -934,7 +949,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file, libFile, configFile]);
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
checkOutputContains(host, errors(line));
checkOutputDoesNotContain(host, errors(line - 2));
host.clearOutput();
@@ -985,7 +1000,7 @@ namespace ts.tscWatch {
const files = [f1, f2, config, libFile];
host.reloadFS(files);
- createWatchWithConfig(config.path, host);
+ createWatchModeWithConfigFile(config.path, host);
const allEmittedLines = getEmittedLines(files);
checkOutputContains(host, allEmittedLines);
@@ -1073,7 +1088,7 @@ namespace ts.tscWatch {
host.reloadFS(firstReloadFileList ? getFiles(firstReloadFileList) : files);
// Initial compile
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
if (firstCompilationEmitFiles) {
checkAffectedLines(host, getFiles(firstCompilationEmitFiles), allEmittedFiles);
}
@@ -1384,11 +1399,11 @@ namespace ts.tscWatch {
// Initial compile
if (configFile) {
- createWatchWithConfig(configFile.path, host);
+ createWatchModeWithConfigFile(configFile.path, host);
}
else {
// First file as the root
- createWatchModeWithoutConfigFile([files[0].path], { listEmittedFiles: true }, host);
+ createWatchModeWithoutConfigFile([files[0].path], host, { listEmittedFiles: true });
}
checkOutputContains(host, allEmittedFiles);