Refactoring so that instead of just using from tsc --watch the new api is accessible all the time

This commit is contained in:
Sheetal Nandi 2017-08-03 19:14:47 -07:00
parent bb91b32a4d
commit 46e3d1c1d9
5 changed files with 800 additions and 742 deletions

View File

@ -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";

View File

@ -1,63 +1,13 @@
/// <reference path="program.ts"/>
/// <reference path="watchedProgram.ts"/>
/// <reference path="commandLineParser.ts"/>
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 <string>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<WatchDirectoryFlags>, 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<FileWatcher>; // Map of file watchers for the missing files
let configFileWatcher: FileWatcher; // watcher for the config file
let watchedWildCardDirectories: Map<WildCardDirectoryWatchers>; // map of watchers for the wild card directories in the config file
let timerToUpdateProgram: any; // timer callback to recompile the program
const sourceFilesCache = createMap<HostFileInfo | string>(); // 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;

View File

@ -37,6 +37,7 @@
"emitter.ts",
"program.ts",
"builder.ts",
"watchedProgram.ts",
"commandLineParser.ts",
"tscLib.ts",
"tsc.ts",

View File

@ -0,0 +1,673 @@
/// <reference path="program.ts" />
/// <reference path="builder.ts" />
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<WatchDirectoryFlags>, 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<FileWatcher>; // Map of file watchers for the missing files
let configFileWatcher: FileWatcher; // watcher for the config file
let watchedWildCardDirectories: Map<WildCardDirectoryWatchers>; // map of watchers for the wild card directories in the config file
let timerToUpdateProgram: any; // timer callback to recompile the program
const sourceFilesCache = createMap<HostFileInfo | string>(); // 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()
};
}
}

View File

@ -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);