Handle watches of missing directories and make project the module resolution host

This commit is contained in:
Sheetal Nandi
2017-08-24 11:50:27 -07:00
parent 5aafd3f06c
commit 17565d8407
17 changed files with 586 additions and 531 deletions

View File

@@ -5,8 +5,6 @@
namespace ts {
/** This is the cache of module/typedirectives resolution that can be retained across program */
export interface ResolutionCache {
setModuleResolutionHost(host: ModuleResolutionHost): void;
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[];
@@ -14,8 +12,6 @@ namespace ts {
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
invalidateResolutionOfFile(filePath: Path): void;
onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder: Path): boolean;
createHasInvalidatedResolution(): HasInvalidatedResolution;
clear(): void;
@@ -33,15 +29,17 @@ namespace ts {
mapLocations: MultiMap<string>;
}
export function createResolutionCache(
toPath: (fileName: string) => Path,
getCompilerOptions: () => CompilerOptions,
watchDirectoryOfFailedLookupLocation: (directory: string) => FileWatcher,
log: (s: string) => void,
projectName?: string,
getGlobalCache?: () => string | undefined): ResolutionCache {
export interface ResolutionCacheHost extends ModuleResolutionHost {
toPath(fileName: string): Path;
getCompilationSettings(): CompilerOptions;
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback): FileWatcher;
onInvalidatedResolution(): void;
getCachedPartialSystem?(): CachedPartialSystem;
projectName?: string;
getGlobalCache?(): string | undefined;
}
let host: ModuleResolutionHost;
export function createResolutionCache(resolutionHost: ResolutionCacheHost): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Map<true> | undefined;
@@ -52,23 +50,16 @@ namespace ts {
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const directoryWatchesOfFailedLookups = createMap<DirectoryWatchesOfFailedLookup>();
return {
setModuleResolutionHost,
startRecordingFilesWithChangedResolutions,
finishRecordingFilesWithChangedResolutions,
resolveModuleNames,
resolveTypeReferenceDirectives,
invalidateResolutionOfFile,
onFileAddOrRemoveInDirectoryOfFailedLookup,
createHasInvalidatedResolution,
clear
};
function setModuleResolutionHost(updatedHost: ModuleResolutionHost) {
host = updatedHost;
}
function clear() {
// Close all the watches for failed lookup locations, irrespective of refcounts for them since this is to clear the cache
clearMap(directoryWatchesOfFailedLookups, closeFileWatcherOf);
@@ -95,16 +86,16 @@ namespace ts {
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host);
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
if (!getGlobalCache) {
if (!resolutionHost.getGlobalCache) {
return primaryResult;
}
// otherwise try to load typings from @types
const globalCache = getGlobalCache();
const globalCache = resolutionHost.getGlobalCache();
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension))) {
// create different collection of failed lookup locations for second pass
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, projectName, compilerOptions, host, globalCache);
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, resolutionHost.projectName, compilerOptions, host, globalCache);
if (resolvedModule) {
return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as Array<string>, failedLookupLocations) };
}
@@ -123,12 +114,12 @@ namespace ts {
getResultFileName: (result: R) => string | undefined,
logChanges: boolean): R[] {
const path = toPath(containingFile);
const path = resolutionHost.toPath(containingFile);
const currentResolutionsInFile = cache.get(path);
const newResolutions: Map<T> = createMap<T>();
const resolvedModules: R[] = [];
const compilerOptions = getCompilerOptions();
const compilerOptions = resolutionHost.getCompilationSettings();
for (const name of names) {
// check if this is a duplicate entry in the list
@@ -140,7 +131,7 @@ namespace ts {
resolution = existingResolution;
}
else {
resolution = loader(name, containingFile, compilerOptions, host);
resolution = loader(name, containingFile, compilerOptions, resolutionHost);
updateFailedLookupLocationWatches(resolution.failedLookupLocations, existingResolution && existingResolution.failedLookupLocations);
}
newResolutions.set(name, resolution);
@@ -214,12 +205,31 @@ namespace ts {
const mapLocations = createMultiMap<string>();
mapLocations.add(failedLookupLocationPath, failedLookupLocation);
directoryWatchesOfFailedLookups.set(dirPath, {
watcher: watchDirectoryOfFailedLookupLocation(getDirectoryPath(failedLookupLocation)),
watcher: createDirectoryWatcher(getDirectoryPath(failedLookupLocation), dirPath),
mapLocations
});
}
}
function createDirectoryWatcher(directory: string, dirPath: Path) {
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrFolder => {
const fileOrFolderPath = resolutionHost.toPath(fileOrFolder);
if (resolutionHost.getCachedPartialSystem) {
// Since the file existance changed, update the sourceFiles cache
resolutionHost.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
}
// If the location results in update to failed lookup, schedule program update
if (dirPath === fileOrFolderPath) {
onAddOrRemoveDirectoryOfFailedLookup(dirPath);
resolutionHost.onInvalidatedResolution();
}
else if (onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolderPath)) {
resolutionHost.onInvalidatedResolution();
}
});
}
function closeFailedLookupLocationWatcher(failedLookupLocation: string, failedLookupLocationPath: Path) {
const dirPath = getDirectoryPath(failedLookupLocationPath);
const watches = directoryWatchesOfFailedLookups.get(dirPath);
@@ -234,13 +244,12 @@ namespace ts {
function withFailedLookupLocations(failedLookupLocations: ReadonlyArray<string> | undefined, fn: FailedLookupLocationAction, startIndex?: number) {
if (failedLookupLocations) {
for (let i = startIndex || 0; i < failedLookupLocations.length; i++) {
fn(failedLookupLocations[i], toPath(failedLookupLocations[i]));
fn(failedLookupLocations[i], resolutionHost.toPath(failedLookupLocations[i]));
}
}
}
function updateFailedLookupLocationWatches(failedLookupLocations: ReadonlyArray<string> | undefined, existingFailedLookupLocations: ReadonlyArray<string> | undefined) {
log(`Resolution cache: Updating...`);
const index = existingFailedLookupLocations && failedLookupLocations ?
findDiffIndex(failedLookupLocations, existingFailedLookupLocations) :
0;
@@ -269,7 +278,7 @@ namespace ts {
if (resolution && !resolution.isInvalidated) {
const result = getResult(resolution);
if (result) {
if (toPath(getResultFileName(result)) === deletedFilePath) {
if (resolutionHost.toPath(getResultFileName(result)) === deletedFilePath) {
resolution.isInvalidated = true;
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(path, true);
}
@@ -281,12 +290,13 @@ namespace ts {
}
function invalidateResolutionCacheOfChangedFailedLookupLocation<T extends NameResolutionWithFailedLookupLocations>(
failedLookupLocationPath: Path,
cache: Map<Map<T>>) {
cache: Map<Map<T>>,
isChangedFailedLookupLocation: (location: string) => boolean
) {
cache.forEach((value, containingFile) => {
if (value) {
value.forEach(resolution => {
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, location => toPath(location) === failedLookupLocationPath)) {
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, isChangedFailedLookupLocation)) {
// Mark the file as needing re-evaluation of module resolution instead of using it blindly.
resolution.isInvalidated = true;
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(containingFile, true);
@@ -306,10 +316,17 @@ namespace ts {
const watches = directoryWatchesOfFailedLookups.get(dirPath);
const isFailedLookupFile = watches.mapLocations.has(fileOrFolder);
if (isFailedLookupFile) {
invalidateResolutionCacheOfChangedFailedLookupLocation(fileOrFolder, resolvedModuleNames);
invalidateResolutionCacheOfChangedFailedLookupLocation(fileOrFolder, resolvedTypeReferenceDirectives);
const isFileOrFolder: (location: string) => boolean = location => resolutionHost.toPath(location) === fileOrFolder;
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedModuleNames, isFileOrFolder);
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedTypeReferenceDirectives, isFileOrFolder);
}
return isFailedLookupFile;
}
function onAddOrRemoveDirectoryOfFailedLookup(dirPath: Path) {
const isInDirPath: (location: string) => boolean = location => getDirectoryPath(resolutionHost.toPath(location)) === dirPath;
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedModuleNames, isInDirPath);
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedTypeReferenceDirectives, isInDirPath);
}
}
}

View File

@@ -161,11 +161,10 @@ namespace ts {
watcher.referenceCount += 1;
return;
}
watcher = _fs.watch(
watcher = fsWatchDirectory(
dirPath || ".",
{ persistent: true },
(eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath)
);
) as DirectoryWatcher;
watcher.referenceCount = 1;
dirWatchers.set(dirPath, watcher);
return;
@@ -232,6 +231,83 @@ namespace ts {
const platform: string = _os.platform();
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
function fsWatchFile(fileName: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher {
_fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged);
return {
close: () => _fs.unwatchFile(fileName, fileChanged)
};
function fileChanged(curr: any, prev: any) {
const isCurrZero = +curr.mtime === 0;
const isPrevZero = +prev.mtime === 0;
const created = !isCurrZero && isPrevZero;
const deleted = isCurrZero && !isPrevZero;
const eventKind = created
? FileWatcherEventKind.Created
: deleted
? FileWatcherEventKind.Deleted
: FileWatcherEventKind.Changed;
if (eventKind === FileWatcherEventKind.Changed && +curr.mtime <= +prev.mtime) {
return;
}
callback(fileName, eventKind);
}
}
function fsWatchDirectory(directoryName: string, callback: (eventName: string, relativeFileName: string) => void, recursive?: boolean): FileWatcher {
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
let options: any;
let watcher = !directoryExists(directoryName) ?
watchMissingDirectory() :
watchPresentDirectory();
return {
close: () => {
watcher.close();
}
};
function watchPresentDirectory(): FileWatcher {
if (options === undefined) {
if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) {
options = { persistent: true, recursive: !!recursive };
}
else {
options = { persistent: true };
}
}
const dirWatcher = _fs.watch(
directoryName,
options,
callback
);
dirWatcher.on("error", () => {
// Deleting file
watcher = watchMissingDirectory();
// Call the callback for current directory
callback("rename", "");
});
return dirWatcher;
}
function watchMissingDirectory(): FileWatcher {
return fsWatchFile(directoryName, (_fileName, eventKind) => {
if (eventKind === FileWatcherEventKind.Created && directoryExists(directoryName)) {
watcher.close();
watcher = watchPresentDirectory();
// Call the callback for current directory
// For now it could be callback for the inner directory creation,
// but just return current directory, better than current no-op
callback("rename", "");
}
});
}
}
function readFile(fileName: string, _encoding?: string): string | undefined {
if (!fileExists(fileName)) {
return undefined;
@@ -349,7 +425,6 @@ namespace ts {
return filter<string>(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory));
}
const noOpFileWatcher: FileWatcher = { close: noop };
const nodeSystem: System = {
args: process.argv.slice(2),
newLine: _os.EOL,
@@ -367,60 +442,21 @@ namespace ts {
};
}
else {
_fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged);
return {
close: () => _fs.unwatchFile(fileName, fileChanged)
};
}
function fileChanged(curr: any, prev: any) {
const isCurrZero = +curr.mtime === 0;
const isPrevZero = +prev.mtime === 0;
const created = !isCurrZero && isPrevZero;
const deleted = isCurrZero && !isPrevZero;
const eventKind = created
? FileWatcherEventKind.Created
: deleted
? FileWatcherEventKind.Deleted
: FileWatcherEventKind.Changed;
if (eventKind === FileWatcherEventKind.Changed && +curr.mtime <= +prev.mtime) {
return;
}
callback(fileName, eventKind);
return fsWatchFile(fileName, callback, pollingInterval);
}
},
watchDirectory: (directoryName, callback, recursive) => {
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
let options: any;
if (!directoryExists(directoryName)) {
// do nothing if target folder does not exist
return noOpFileWatcher;
}
if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) {
options = { persistent: true, recursive: !!recursive };
}
else {
options = { persistent: true };
}
return _fs.watch(
directoryName,
options,
(eventName: string, relativeFileName: string) => {
// In watchDirectory we only care about adding and removing files (when event name is
// "rename"); changes made within files are handled by corresponding fileWatchers (when
// event name is "change")
if (eventName === "rename") {
// When deleting a file, the passed baseFileName is null
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
}
return fsWatchDirectory(directoryName, (eventName, relativeFileName) => {
// In watchDirectory we only care about adding and removing files (when event name is
// "rename"); changes made within files are handled by corresponding fileWatchers (when
// event name is "change")
if (eventName === "rename") {
// When deleting a file, the passed baseFileName is null
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
}
);
}, recursive);
},
resolvePath: path => _path.resolve(path),
fileExists,

View File

@@ -3576,9 +3576,7 @@ namespace ts {
export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0;
return host.watchDirectory(directory, fileName => {
cb(getNormalizedAbsolutePath(fileName, directory));
}, recursive);
return host.watchDirectory(directory, cb, recursive);
}
export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher {

View File

@@ -245,7 +245,6 @@ namespace ts {
const sourceFilesCache = createMap<HostFileInfo | string>(); // Cache that stores the source file and version info
let missingFilePathsRequestedForRelease: Path[]; // These paths are held temparirly so that we can remove the entry from source file cache if the file is not tracked by missing files
let hasInvalidatedResolution: HasInvalidatedResolution; // Passed along to see if source file has invalidated resolutions
let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
@@ -257,20 +256,47 @@ namespace ts {
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
const host = configFileName ? createCachedPartialSystem(system) : system;
const partialSystem = configFileName ? createCachedPartialSystem(system) : system;
if (configFileName) {
configFileWatcher = watchFile(system, configFileName, scheduleProgramReload, writeLog);
}
const currentDirectory = host.getCurrentDirectory();
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
// Cache for the module resolution
const resolutionCache = createResolutionCache(
fileName => toPath(fileName),
() => compilerOptions,
const getCurrentDirectory = memoize(() => partialSystem.getCurrentDirectory());
const realpath = system.realpath && ((path: string) => system.realpath(path));
const getCachedPartialSystem = configFileName && (() => partialSystem as CachedPartialSystem);
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
let newLine = getNewLineCharacter(compilerOptions, system);
const compilerHost: CompilerHost & ResolutionCacheHost = {
// Members for CompilerHost
getSourceFile: getVersionedSourceFile,
getSourceFileByPath: getVersionedSourceFileByPath,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { },
getCurrentDirectory,
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
fileExists,
readFile,
trace,
directoryExists,
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
getDirectories,
realpath,
resolveTypeReferenceDirectives,
resolveModuleNames,
onReleaseOldSourceFile,
// Members for ResolutionCacheHost
toPath,
getCompilationSettings: () => compilerOptions,
watchDirectoryOfFailedLookupLocation,
writeLog
);
getCachedPartialSystem,
onInvalidatedResolution: scheduleProgramUpdate
};
// Cache for the module resolution
const resolutionCache = createResolutionCache(compilerHost);
// 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);
@@ -285,14 +311,15 @@ namespace ts {
function synchronizeProgram() {
writeLog(`Synchronizing program`);
hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution();
if (hasChangedCompilerOptions) {
newLine = getNewLineCharacter(compilerOptions, system);
}
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution();
if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution)) {
return;
}
// Create the compiler host
const compilerHost = createWatchedCompilerHost(compilerOptions);
resolutionCache.setModuleResolutionHost(compilerHost);
if (hasChangedCompilerOptions && changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
resolutionCache.clear();
}
@@ -300,6 +327,7 @@ namespace ts {
beforeCompile(compilerOptions);
// Compile the program
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
program = createProgram(rootFileNames, compilerOptions, compilerHost, program);
builder.onProgramUpdateGraph(program, hasInvalidatedResolution);
@@ -309,7 +337,7 @@ namespace ts {
// These are the paths that program creater told us as not in use any more but were missing on the disk.
// We didnt remove the entry for them from sourceFiles cache so that we dont have to do File IO,
// if there is already watcher for it (for missing files)
// At that point our watches were updated, hence now we know that these paths are not tracked and need to be removed
// At this point our watches were updated, hence now we know that these paths are not tracked and need to be removed
// so that at later time we have correct result of their presence
for (const missingFilePath of missingFilePathsRequestedForRelease) {
if (!missingFilesMap.has(missingFilePath)) {
@@ -319,40 +347,12 @@ namespace ts {
missingFilePathsRequestedForRelease = undefined;
}
afterCompile(host, program, builder);
afterCompile(partialSystem, program, builder);
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
}
function createWatchedCompilerHost(options: CompilerOptions): CompilerHost {
const newLine = getNewLineCharacter(options, system);
const realpath = system.realpath && ((path: string) => system.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: () => system.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
fileExists,
readFile: fileName => system.readFile(fileName),
trace: (s: string) => system.write(s + newLine),
directoryExists: directoryName => host.directoryExists(directoryName),
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
getDirectories: (path: string) => host.getDirectories(path),
realpath,
resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile),
resolveModuleNames: (moduleNames, containingFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ false),
onReleaseOldSourceFile,
hasInvalidatedResolution
};
}
function toPath(fileName: string) {
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName);
}
function fileExists(fileName: string) {
@@ -362,7 +362,31 @@ namespace ts {
return !isString(hostSourceFileInfo);
}
return host.fileExists(fileName);
return partialSystem.fileExists(fileName);
}
function directoryExists(directoryName: string) {
return partialSystem.directoryExists(directoryName);
}
function readFile(fileName: string) {
return system.readFile(fileName);
}
function trace(s: string) {
return system.write(s + newLine);
}
function getDirectories(path: string) {
return partialSystem.getDirectories(path);
}
function resolveModuleNames(moduleNames: string[], containingFile: string) {
return resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ false);
}
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string) {
return resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
}
function getDefaultLibLocation(): string {
@@ -503,7 +527,7 @@ namespace ts {
writeLog(`Reloading config file: ${configFileName}`);
needsReload = false;
const cachedHost = host as CachedPartialSystem;
const cachedHost = partialSystem as CachedPartialSystem;
cachedHost.clearCache();
const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic);
rootFileNames = configParseResult.fileNames;
@@ -548,26 +572,12 @@ namespace ts {
function updateCachedSystemWithFile(fileName: string, path: Path, eventKind: FileWatcherEventKind) {
if (configFileName) {
(host as CachedPartialSystem).addOrDeleteFile(fileName, path, eventKind);
(partialSystem as CachedPartialSystem).addOrDeleteFile(fileName, path, eventKind);
}
}
function watchDirectoryOfFailedLookupLocation(directory: string) {
return watchDirectory(system, directory, onFileAddOrRemoveInDirectoryOfFailedLookup, WatchDirectoryFlags.None, writeLog);
}
function onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder: string) {
const fileOrFolderPath = toPath(fileOrFolder);
if (configFileName) {
// Since the file existance changed, update the sourceFiles cache
(host as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
}
// If the location results in update to failed lookup, schedule program update
if (resolutionCache.onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolderPath)) {
scheduleProgramUpdate();
}
function watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback) {
return watchDirectory(system, directory, cb, WatchDirectoryFlags.None, writeLog);
}
function watchMissingFilePath(missingFilePath: Path) {
@@ -593,42 +603,45 @@ namespace ts {
updateWatchingWildcardDirectories(
watchedWildcardDirectories || (watchedWildcardDirectories = createMap()),
createMapFromTemplate(configFileWildCardDirectories),
watchWildCardDirectory
watchWildcardDirectory
);
}
function watchWildCardDirectory(directory: string, flags: WatchDirectoryFlags) {
return watchDirectory(system, directory, onFileAddOrRemoveInWatchedDirectory, flags, writeLog);
}
function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) {
return watchDirectory(
system,
directory,
fileOrFolder => {
Debug.assert(!!configFileName);
function onFileAddOrRemoveInWatchedDirectory(fileOrFolder: string) {
Debug.assert(!!configFileName);
const fileOrFolderPath = toPath(fileOrFolder);
const fileOrFolderPath = toPath(fileOrFolder);
// Since the file existance changed, update the sourceFiles cache
(partialSystem as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
removeSourceFile(fileOrFolderPath);
// Since the file existance changed, update the sourceFiles cache
(host as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
removeSourceFile(fileOrFolderPath);
// If the the added or created file or folder is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
if (fileOrFolderPath !== directory && !isSupportedSourceFileName(fileOrFolder, compilerOptions)) {
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrFolder}`);
return;
}
// 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 (fileOrFolder && !isSupportedSourceFileName(fileOrFolder, compilerOptions)) {
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrFolder}`);
return;
}
// Reload is pending, do the reload
if (!needsReload) {
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, partialSystem);
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
rootFileNames = result.fileNames;
// Reload is pending, do the reload
if (!needsReload) {
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, host);
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
rootFileNames = result.fileNames;
// Schedule Update the program
scheduleProgramUpdate();
}
// Schedule Update the program
scheduleProgramUpdate();
}
},
flags,
writeLog
);
}
function computeHash(data: string) {