mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Refactoring so that instead of just using from tsc --watch the new api is accessible all the time
This commit is contained in:
parent
bb91b32a4d
commit
46e3d1c1d9
@ -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";
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
"builder.ts",
|
||||
"watchedProgram.ts",
|
||||
"commandLineParser.ts",
|
||||
"tscLib.ts",
|
||||
"tsc.ts",
|
||||
|
||||
673
src/compiler/watchedProgram.ts
Normal file
673
src/compiler/watchedProgram.ts
Normal 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()
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user