getEditsForFileRename: Do fresh module resolution instead of relying on cache (#24211)

* getEditsForFileRename: Do fresh module resolution instead of relying on cache

* Add host.resolveModuleNameWithFailedLookupLocations method

* Make host.resolveModuleNameWithFailedLookupLocations mandatory, and implement for Project

* Add test, and no need to check host.fileExists

* Change method name and always use cache

* Update name in string
This commit is contained in:
Andy
2018-05-18 16:42:42 -07:00
committed by GitHub
parent 3eb66da155
commit 4c22bf786e
7 changed files with 56 additions and 6 deletions
+10 -3
View File
@@ -6,6 +6,7 @@ namespace ts {
finishRecordingFilesWithChangedResolutions(): Path[];
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations;
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
invalidateResolutionOfFile(filePath: Path): void;
@@ -73,7 +74,7 @@ namespace ts {
export const maxNumberOfFilesToIterateForInvalidation = 256;
type GetResolutionWithResolvedFileName<T extends ResolutionWithFailedLookupLocations = ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName = ResolutionWithResolvedFileName> =
(resolution: T) => R;
(resolution: T) => R | undefined;
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string, logChangesWhenResolvingModule: boolean): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
@@ -124,6 +125,7 @@ namespace ts {
startCachingPerDirectoryResolution: clearPerDirectoryResolutions,
finishCachingPerDirectoryResolution,
resolveModuleNames,
getResolvedModuleWithFailedLookupLocationsFromCache,
resolveTypeReferenceDirectives,
removeResolutionsOfFile,
invalidateResolutionOfFile,
@@ -320,7 +322,7 @@ namespace ts {
}
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
return resolveNamesWithLocalCache(
return resolveNamesWithLocalCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations, ResolvedTypeReferenceDirective>(
typeDirectiveNames, containingFile,
resolvedTypeReferenceDirectives, perDirectoryResolvedTypeReferenceDirectives,
resolveTypeReferenceDirective, getResolvedTypeReferenceDirective,
@@ -329,7 +331,7 @@ namespace ts {
}
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[] {
return resolveNamesWithLocalCache(
return resolveNamesWithLocalCache<ResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>(
moduleNames, containingFile,
resolvedModuleNames, perDirectoryResolvedModuleNames,
resolveModuleName, getResolvedModule,
@@ -337,6 +339,11 @@ namespace ts {
);
}
function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined {
const cache = resolvedModuleNames.get(resolutionHost.toPath(containingFile));
return cache && cache.get(moduleName);
}
function isNodeModulesDirectory(dirPath: Path) {
return endsWith(dirPath, "/node_modules");
}
@@ -8383,4 +8383,35 @@ new C();`
verifyCompletionListWithNewFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.DynamicPolling);
});
});
describe("tsserverProjectSystem getEditsForFileRename", () => {
it("works for host implementing 'resolveModuleNames' and 'getResolvedModuleWithFailedLookupLocationsFromCache'", () => {
const userTs: File = {
path: "/user.ts",
content: 'import { x } from "./old";',
};
const host = createServerHost([userTs]);
const projectService = createProjectService(host);
projectService.openClientFile(userTs.path);
const project = first(projectService.inferredProjects);
Debug.assert(!!project.resolveModuleNames);
const edits = project.getLanguageService().getEditsForFileRename("/old.ts", "/new.ts", testFormatOptions);
assert.deepEqual<ReadonlyArray<FileTextChanges>>(edits, [{
fileName: "/user.ts",
textChanges: [{
span: textSpanFromSubstring(userTs.content, "./old"),
newText: "./new",
}],
}]);
});
});
function textSpanFromSubstring(str: string, substring: string): TextSpan {
const start = str.indexOf(substring);
Debug.assert(start !== -1);
return createTextSpan(start, substring.length);
}
}
+4
View File
@@ -358,6 +358,10 @@ namespace ts.server {
return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames);
}
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations {
return this.resolutionCache.getResolvedModuleWithFailedLookupLocationsFromCache(moduleName, containingFile);
}
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
return this.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
}
+5 -3
View File
@@ -4,7 +4,7 @@ namespace ts {
const pathUpdater = getPathUpdater(oldFilePath, newFilePath, host);
return textChanges.ChangeTracker.with({ host, formatContext }, changeTracker => {
updateTsconfigFiles(program, changeTracker, oldFilePath, newFilePath);
for (const { sourceFile, toUpdate } of getImportsToUpdate(program, oldFilePath)) {
for (const { sourceFile, toUpdate } of getImportsToUpdate(program, oldFilePath, host)) {
const newPath = pathUpdater(isRef(toUpdate) ? toUpdate.fileName : toUpdate.text);
if (newPath !== undefined) {
const range = isRef(toUpdate) ? toUpdate : createStringRange(toUpdate, sourceFile);
@@ -30,7 +30,7 @@ namespace ts {
return "fileName" in toUpdate;
}
function getImportsToUpdate(program: Program, oldFilePath: string): ReadonlyArray<ToUpdate> {
function getImportsToUpdate(program: Program, oldFilePath: string, host: LanguageServiceHost): ReadonlyArray<ToUpdate> {
const checker = program.getTypeChecker();
const result: ToUpdate[] = [];
for (const sourceFile of program.getSourceFiles()) {
@@ -44,7 +44,9 @@ namespace ts {
// If it resolved to something already, ignore.
if (checker.getSymbolAtLocation(importStringLiteral)) continue;
const resolved = program.getResolvedModuleWithFailedLookupLocationsFromCache(importStringLiteral.text, sourceFile.fileName);
const resolved = host.resolveModuleNames
? host.getResolvedModuleWithFailedLookupLocationsFromCache && host.getResolvedModuleWithFailedLookupLocationsFromCache(importStringLiteral.text, sourceFile.fileName)
: program.getResolvedModuleWithFailedLookupLocationsFromCache(importStringLiteral.text, sourceFile.fileName);
if (resolved && contains(resolved.failedLookupLocations, oldFilePath)) {
result.push({ sourceFile, toUpdate: importStringLiteral });
}
+3
View File
@@ -207,8 +207,11 @@ namespace ts {
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
* if implementation is omitted then language service will use built-in module resolution logic and get answers to
* host specific questions using 'getScriptSnapshot'.
*
* If this is implemented, `getResolvedModuleWithFailedLookupLocationsFromCache` should be too.
*/
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string): ResolvedModuleWithFailedLookupLocations;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
+2
View File
@@ -4477,6 +4477,7 @@ declare namespace ts {
fileExists?(path: string): boolean;
getTypeRootsVersion?(): number;
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string): ResolvedModuleWithFailedLookupLocations;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getDirectories?(directoryName: string): string[];
/**
@@ -7864,6 +7865,7 @@ declare namespace ts.server {
readFile(fileName: string): string | undefined;
fileExists(file: string): boolean;
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModuleFull[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations;
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
directoryExists(path: string): boolean;
getDirectories(path: string): string[];
+1
View File
@@ -4477,6 +4477,7 @@ declare namespace ts {
fileExists?(path: string): boolean;
getTypeRootsVersion?(): number;
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string): ResolvedModuleWithFailedLookupLocations;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getDirectories?(directoryName: string): string[];
/**