Skip to content

Commit 903e681

Browse files
author
Andy
authored
Redo resolution on ATA when previous resolution was to '.js' file (microsoft#28236)
* Redo resolution on ATA when previous resolution was to '.js' file * Use a separate test case
1 parent 3458360 commit 903e681

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

src/compiler/resolutionCache.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ namespace ts {
248248
perDirectoryCacheWithRedirects: CacheWithRedirects<Map<T>>,
249249
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T,
250250
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
251+
shouldRetryResolution: (t: T) => boolean,
251252
reusedNames: ReadonlyArray<string> | undefined,
252253
logChanges: boolean): (R | undefined)[] {
253254

@@ -260,7 +261,7 @@ namespace ts {
260261
perDirectoryResolution = createMap();
261262
perDirectoryCache.set(dirPath, perDirectoryResolution);
262263
}
263-
const resolvedModules: R[] = [];
264+
const resolvedModules: (R | undefined)[] = [];
264265
const compilerOptions = resolutionHost.getCompilationSettings();
265266
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
266267

@@ -278,7 +279,7 @@ namespace ts {
278279
if (!seenNamesInFile.has(name) &&
279280
allFilesHaveInvalidatedResolution || unmatchedRedirects || !resolution || resolution.isInvalidated ||
280281
// If the name is unresolved import that was invalidated, recalculate
281-
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && !getResolutionWithResolvedFileName(resolution))) {
282+
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
282283
const existingResolution = resolution;
283284
const resolutionInDirectory = perDirectoryResolution.get(name);
284285
if (resolutionInDirectory) {
@@ -302,7 +303,7 @@ namespace ts {
302303
}
303304
Debug.assert(resolution !== undefined && !resolution.isInvalidated);
304305
seenNamesInFile.set(name, true);
305-
resolvedModules.push(getResolutionWithResolvedFileName(resolution)!); // TODO: GH#18217
306+
resolvedModules.push(getResolutionWithResolvedFileName(resolution));
306307
}
307308

308309
// Stop watching and remove the unused name
@@ -339,6 +340,7 @@ namespace ts {
339340
typeDirectiveNames, containingFile, redirectedReference,
340341
resolvedTypeReferenceDirectives, perDirectoryResolvedTypeReferenceDirectives,
341342
resolveTypeReferenceDirective, getResolvedTypeReferenceDirective,
343+
/*shouldRetryResolution*/ resolution => resolution.resolvedTypeReferenceDirective === undefined,
342344
/*reusedNames*/ undefined, /*logChanges*/ false
343345
);
344346
}
@@ -348,6 +350,7 @@ namespace ts {
348350
moduleNames, containingFile, redirectedReference,
349351
resolvedModuleNames, perDirectoryResolvedModuleNames,
350352
resolveModuleName, getResolvedModule,
353+
/*shouldRetryResolution*/ resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
351354
reusedNames, logChangesWhenResolvingModule
352355
);
353356
}

src/server/project.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,9 @@ namespace ts.server {
11861186
let unresolvedImports: string[] | undefined;
11871187
file.resolvedModules.forEach((resolvedModule, name) => {
11881188
// pick unresolved non-relative names
1189-
if (!resolvedModule && !isExternalModuleNameRelative(name) && !ambientModules.some(m => m === name)) {
1189+
if ((!resolvedModule || !resolutionExtensionIsTSOrJson(resolvedModule.extension)) &&
1190+
!isExternalModuleNameRelative(name) &&
1191+
!ambientModules.some(m => m === name)) {
11901192
unresolvedImports = append(unresolvedImports, parsePackageName(name).packageName);
11911193
}
11921194
});

src/testRunner/unittests/typingsInstaller.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,50 @@ namespace ts.projectSystem {
986986
checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]);
987987
});
988988

989+
it("should redo resolution that resolved to '.js' file after typings are installed", () => {
990+
const file: TestFSWithWatch.File = {
991+
path: "/a/b/app.js",
992+
content: `
993+
import * as commander from "commander";`
994+
};
995+
const cachePath = "/a/cache";
996+
const commanderJS: TestFSWithWatch.File = {
997+
path: "/node_modules/commander/index.js",
998+
content: "module.exports = 0",
999+
};
1000+
1001+
const typeNames: ReadonlyArray<string> = ["commander"];
1002+
const typePath = (name: string): string => `${cachePath}/node_modules/@types/${name}/index.d.ts`;
1003+
const host = createServerHost([file, commanderJS]);
1004+
const installer = new (class extends Installer {
1005+
constructor() {
1006+
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry(...typeNames) });
1007+
}
1008+
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
1009+
const installedTypings = typeNames.map(name => `@types/${name}`);
1010+
const typingFiles = typeNames.map((name): TestFSWithWatch.File => ({ path: typePath(name), content: "" }));
1011+
executeCommand(this, host, installedTypings, typingFiles, cb);
1012+
}
1013+
})();
1014+
const service = createProjectService(host, { typingsInstaller: installer });
1015+
service.openClientFile(file.path);
1016+
1017+
checkWatchedFiles(host, [...flatMap(["/a/b", "/a", ""], x => [x + "/tsconfig.json", x + "/jsconfig.json"]), "/a/lib/lib.d.ts"]);
1018+
checkWatchedDirectories(host, [], /*recursive*/ false);
1019+
// Does not include cachePath because that is handled by typingsInstaller
1020+
checkWatchedDirectories(host, ["/node_modules", "/a/b/node_modules", "/a/b/node_modules/@types", "/a/b/bower_components"], /*recursive*/ true);
1021+
1022+
service.checkNumberOfProjects({ inferredProjects: 1 });
1023+
checkProjectActualFiles(service.inferredProjects[0], [file.path, commanderJS.path]);
1024+
1025+
installer.installAll(/*expectedCount*/1);
1026+
for (const name of typeNames) {
1027+
assert.isTrue(host.fileExists(typePath(name)), `typings for '${name}' should be created`);
1028+
}
1029+
host.checkTimeoutQueueLengthAndRun(2);
1030+
checkProjectActualFiles(service.inferredProjects[0], [file.path, ...typeNames.map(typePath)]);
1031+
});
1032+
9891033
it("should pick typing names from non-relative unresolved imports", () => {
9901034
const f1 = {
9911035
path: "/a/b/app.js",

0 commit comments

Comments
 (0)