Skip to content

Commit 17565d8

Browse files
committed
Handle watches of missing directories and make project the module resolution host
1 parent 5aafd3f commit 17565d8

17 files changed

Lines changed: 586 additions & 531 deletions

Jakefile.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ var harnessSources = harnessCoreSources.concat([
149149
"utilities.ts",
150150
"scriptVersionCache.ts",
151151
"scriptInfo.ts",
152-
"lsHost.ts",
153152
"project.ts",
154153
"typingsCache.ts",
155154
"editorServices.ts",

src/compiler/resolutionCache.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
namespace ts {
66
/** This is the cache of module/typedirectives resolution that can be retained across program */
77
export interface ResolutionCache {
8-
setModuleResolutionHost(host: ModuleResolutionHost): void;
9-
108
startRecordingFilesWithChangedResolutions(): void;
119
finishRecordingFilesWithChangedResolutions(): Path[];
1210

1311
resolveModuleNames(moduleNames: string[], containingFile: string, logChanges: boolean): ResolvedModuleFull[];
1412
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
1513

1614
invalidateResolutionOfFile(filePath: Path): void;
17-
onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder: Path): boolean;
18-
1915
createHasInvalidatedResolution(): HasInvalidatedResolution;
2016

2117
clear(): void;
@@ -33,15 +29,17 @@ namespace ts {
3329
mapLocations: MultiMap<string>;
3430
}
3531

36-
export function createResolutionCache(
37-
toPath: (fileName: string) => Path,
38-
getCompilerOptions: () => CompilerOptions,
39-
watchDirectoryOfFailedLookupLocation: (directory: string) => FileWatcher,
40-
log: (s: string) => void,
41-
projectName?: string,
42-
getGlobalCache?: () => string | undefined): ResolutionCache {
32+
export interface ResolutionCacheHost extends ModuleResolutionHost {
33+
toPath(fileName: string): Path;
34+
getCompilationSettings(): CompilerOptions;
35+
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback): FileWatcher;
36+
onInvalidatedResolution(): void;
37+
getCachedPartialSystem?(): CachedPartialSystem;
38+
projectName?: string;
39+
getGlobalCache?(): string | undefined;
40+
}
4341

44-
let host: ModuleResolutionHost;
42+
export function createResolutionCache(resolutionHost: ResolutionCacheHost): ResolutionCache {
4543
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
4644
let filesWithInvalidatedResolutions: Map<true> | undefined;
4745

@@ -52,23 +50,16 @@ namespace ts {
5250
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
5351

5452
const directoryWatchesOfFailedLookups = createMap<DirectoryWatchesOfFailedLookup>();
55-
5653
return {
57-
setModuleResolutionHost,
5854
startRecordingFilesWithChangedResolutions,
5955
finishRecordingFilesWithChangedResolutions,
6056
resolveModuleNames,
6157
resolveTypeReferenceDirectives,
6258
invalidateResolutionOfFile,
63-
onFileAddOrRemoveInDirectoryOfFailedLookup,
6459
createHasInvalidatedResolution,
6560
clear
6661
};
6762

68-
function setModuleResolutionHost(updatedHost: ModuleResolutionHost) {
69-
host = updatedHost;
70-
}
71-
7263
function clear() {
7364
// Close all the watches for failed lookup locations, irrespective of refcounts for them since this is to clear the cache
7465
clearMap(directoryWatchesOfFailedLookups, closeFileWatcherOf);
@@ -95,16 +86,16 @@ namespace ts {
9586
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
9687
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host);
9788
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
98-
if (!getGlobalCache) {
89+
if (!resolutionHost.getGlobalCache) {
9990
return primaryResult;
10091
}
10192

10293
// otherwise try to load typings from @types
103-
const globalCache = getGlobalCache();
94+
const globalCache = resolutionHost.getGlobalCache();
10495
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension))) {
10596
// create different collection of failed lookup locations for second pass
10697
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
107-
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, projectName, compilerOptions, host, globalCache);
98+
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, resolutionHost.projectName, compilerOptions, host, globalCache);
10899
if (resolvedModule) {
109100
return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as Array<string>, failedLookupLocations) };
110101
}
@@ -123,12 +114,12 @@ namespace ts {
123114
getResultFileName: (result: R) => string | undefined,
124115
logChanges: boolean): R[] {
125116

126-
const path = toPath(containingFile);
117+
const path = resolutionHost.toPath(containingFile);
127118
const currentResolutionsInFile = cache.get(path);
128119

129120
const newResolutions: Map<T> = createMap<T>();
130121
const resolvedModules: R[] = [];
131-
const compilerOptions = getCompilerOptions();
122+
const compilerOptions = resolutionHost.getCompilationSettings();
132123

133124
for (const name of names) {
134125
// check if this is a duplicate entry in the list
@@ -140,7 +131,7 @@ namespace ts {
140131
resolution = existingResolution;
141132
}
142133
else {
143-
resolution = loader(name, containingFile, compilerOptions, host);
134+
resolution = loader(name, containingFile, compilerOptions, resolutionHost);
144135
updateFailedLookupLocationWatches(resolution.failedLookupLocations, existingResolution && existingResolution.failedLookupLocations);
145136
}
146137
newResolutions.set(name, resolution);
@@ -214,12 +205,31 @@ namespace ts {
214205
const mapLocations = createMultiMap<string>();
215206
mapLocations.add(failedLookupLocationPath, failedLookupLocation);
216207
directoryWatchesOfFailedLookups.set(dirPath, {
217-
watcher: watchDirectoryOfFailedLookupLocation(getDirectoryPath(failedLookupLocation)),
208+
watcher: createDirectoryWatcher(getDirectoryPath(failedLookupLocation), dirPath),
218209
mapLocations
219210
});
220211
}
221212
}
222213

214+
function createDirectoryWatcher(directory: string, dirPath: Path) {
215+
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrFolder => {
216+
const fileOrFolderPath = resolutionHost.toPath(fileOrFolder);
217+
if (resolutionHost.getCachedPartialSystem) {
218+
// Since the file existance changed, update the sourceFiles cache
219+
resolutionHost.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
220+
}
221+
222+
// If the location results in update to failed lookup, schedule program update
223+
if (dirPath === fileOrFolderPath) {
224+
onAddOrRemoveDirectoryOfFailedLookup(dirPath);
225+
resolutionHost.onInvalidatedResolution();
226+
}
227+
else if (onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolderPath)) {
228+
resolutionHost.onInvalidatedResolution();
229+
}
230+
});
231+
}
232+
223233
function closeFailedLookupLocationWatcher(failedLookupLocation: string, failedLookupLocationPath: Path) {
224234
const dirPath = getDirectoryPath(failedLookupLocationPath);
225235
const watches = directoryWatchesOfFailedLookups.get(dirPath);
@@ -234,13 +244,12 @@ namespace ts {
234244
function withFailedLookupLocations(failedLookupLocations: ReadonlyArray<string> | undefined, fn: FailedLookupLocationAction, startIndex?: number) {
235245
if (failedLookupLocations) {
236246
for (let i = startIndex || 0; i < failedLookupLocations.length; i++) {
237-
fn(failedLookupLocations[i], toPath(failedLookupLocations[i]));
247+
fn(failedLookupLocations[i], resolutionHost.toPath(failedLookupLocations[i]));
238248
}
239249
}
240250
}
241251

242252
function updateFailedLookupLocationWatches(failedLookupLocations: ReadonlyArray<string> | undefined, existingFailedLookupLocations: ReadonlyArray<string> | undefined) {
243-
log(`Resolution cache: Updating...`);
244253
const index = existingFailedLookupLocations && failedLookupLocations ?
245254
findDiffIndex(failedLookupLocations, existingFailedLookupLocations) :
246255
0;
@@ -269,7 +278,7 @@ namespace ts {
269278
if (resolution && !resolution.isInvalidated) {
270279
const result = getResult(resolution);
271280
if (result) {
272-
if (toPath(getResultFileName(result)) === deletedFilePath) {
281+
if (resolutionHost.toPath(getResultFileName(result)) === deletedFilePath) {
273282
resolution.isInvalidated = true;
274283
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(path, true);
275284
}
@@ -281,12 +290,13 @@ namespace ts {
281290
}
282291

283292
function invalidateResolutionCacheOfChangedFailedLookupLocation<T extends NameResolutionWithFailedLookupLocations>(
284-
failedLookupLocationPath: Path,
285-
cache: Map<Map<T>>) {
293+
cache: Map<Map<T>>,
294+
isChangedFailedLookupLocation: (location: string) => boolean
295+
) {
286296
cache.forEach((value, containingFile) => {
287297
if (value) {
288298
value.forEach(resolution => {
289-
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, location => toPath(location) === failedLookupLocationPath)) {
299+
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, isChangedFailedLookupLocation)) {
290300
// Mark the file as needing re-evaluation of module resolution instead of using it blindly.
291301
resolution.isInvalidated = true;
292302
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(containingFile, true);
@@ -306,10 +316,17 @@ namespace ts {
306316
const watches = directoryWatchesOfFailedLookups.get(dirPath);
307317
const isFailedLookupFile = watches.mapLocations.has(fileOrFolder);
308318
if (isFailedLookupFile) {
309-
invalidateResolutionCacheOfChangedFailedLookupLocation(fileOrFolder, resolvedModuleNames);
310-
invalidateResolutionCacheOfChangedFailedLookupLocation(fileOrFolder, resolvedTypeReferenceDirectives);
319+
const isFileOrFolder: (location: string) => boolean = location => resolutionHost.toPath(location) === fileOrFolder;
320+
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedModuleNames, isFileOrFolder);
321+
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedTypeReferenceDirectives, isFileOrFolder);
311322
}
312323
return isFailedLookupFile;
313324
}
325+
326+
function onAddOrRemoveDirectoryOfFailedLookup(dirPath: Path) {
327+
const isInDirPath: (location: string) => boolean = location => getDirectoryPath(resolutionHost.toPath(location)) === dirPath;
328+
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedModuleNames, isInDirPath);
329+
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedTypeReferenceDirectives, isInDirPath);
330+
}
314331
}
315332
}

0 commit comments

Comments
 (0)