Skip to content

Commit 8308ab3

Browse files
author
Andy Hanson
committed
When looking up modules in @types, only look for index.d.ts and package.json.
1 parent 1bc4ab0 commit 8308ab3

13 files changed

+56
-291
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 56 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ namespace ts {
6262

6363
interface ModuleResolutionState {
6464
host: ModuleResolutionHost;
65-
// We only use this subset of the compiler options.
66-
compilerOptions: { rootDirs?: string[], baseUrl?: string, paths?: MapLike<string[]> };
65+
compilerOptions: CompilerOptions;
6766
traceEnabled: boolean;
6867
}
6968

@@ -128,7 +127,9 @@ namespace ts {
128127
currentDirectory = host.getCurrentDirectory();
129128
}
130129

131-
return currentDirectory !== undefined && getDefaultTypeRoots(currentDirectory, host);
130+
if (currentDirectory !== undefined) {
131+
return getDefaultTypeRoots(currentDirectory, host);
132+
}
132133
}
133134

134135
/**
@@ -142,20 +143,12 @@ namespace ts {
142143
}
143144

144145
let typeRoots: string[];
145-
146-
while (true) {
147-
const atTypes = combinePaths(currentDirectory, nodeModulesAtTypes);
146+
forEachAncestorDirectory(currentDirectory, directory => {
147+
const atTypes = combinePaths(directory, nodeModulesAtTypes);
148148
if (host.directoryExists(atTypes)) {
149149
(typeRoots || (typeRoots = [])).push(atTypes);
150150
}
151-
152-
const parent = getDirectoryPath(currentDirectory);
153-
if (parent === currentDirectory) {
154-
break;
155-
}
156-
currentDirectory = parent;
157-
}
158-
151+
});
159152
return typeRoots;
160153
}
161154
const nodeModulesAtTypes = combinePaths("node_modules", "@types");
@@ -233,7 +226,7 @@ namespace ts {
233226
if (traceEnabled) {
234227
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
235228
}
236-
resolvedFile = resolvedTypeScriptOnly(loadModuleFromNodeModules(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState, /*checkOneLevel*/ false));
229+
resolvedFile = resolvedTypeScriptOnly(loadModuleFromNodeModules(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState));
237230
if (traceEnabled) {
238231
if (resolvedFile) {
239232
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
@@ -562,7 +555,7 @@ namespace ts {
562555
if (traceEnabled) {
563556
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
564557
}
565-
const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state, /*checkOneLevel*/ false);
558+
const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state);
566559
return resolved && { resolved, isExternalLibraryImport: true };
567560
}
568561
else {
@@ -649,7 +642,7 @@ namespace ts {
649642
}
650643

651644
/** Return the file if it exists. */
652-
function tryFile(fileName: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
645+
function tryFile(fileName: string, failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
653646
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
654647
if (state.traceEnabled) {
655648
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
@@ -660,12 +653,12 @@ namespace ts {
660653
if (state.traceEnabled) {
661654
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
662655
}
663-
failedLookupLocation.push(fileName);
656+
failedLookupLocations.push(fileName);
664657
return undefined;
665658
}
666659
}
667660

668-
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
661+
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
669662
const packageJsonPath = pathToPackageJson(candidate);
670663
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
671664

@@ -677,12 +670,12 @@ namespace ts {
677670
if (typesFile) {
678671
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
679672
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
680-
const fromFile = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state);
673+
const fromFile = tryFile(typesFile, failedLookupLocations, onlyRecordFailures, state);
681674
if (fromFile) {
682675
// Note: this would allow a package.json to specify a ".js" file as typings. Maybe that should be forbidden.
683676
return resolvedFromAnyFile(fromFile);
684677
}
685-
const x = tryAddingExtensions(typesFile, Extensions.TypeScript, failedLookupLocation, onlyRecordFailures, state);
678+
const x = tryAddingExtensions(typesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
686679
if (x) {
687680
return x;
688681
}
@@ -698,10 +691,10 @@ namespace ts {
698691
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
699692
}
700693
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
701-
failedLookupLocation.push(packageJsonPath);
694+
failedLookupLocations.push(packageJsonPath);
702695
}
703696

704-
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, !directoryExists, state);
697+
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
705698
}
706699

707700
function pathToPackageJson(directory: string): string {
@@ -717,34 +710,30 @@ namespace ts {
717710
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
718711
}
719712

720-
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean): Resolved | undefined {
721-
return loadModuleFromNodeModulesWorker(extensions, moduleName, directory, failedLookupLocations, state, checkOneLevel, /*typesOnly*/ false);
713+
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): Resolved | undefined {
714+
return loadModuleFromNodeModulesWorker(extensions, moduleName, directory, failedLookupLocations, state, /*typesOnly*/ false);
722715
}
723716
function loadModuleFromNodeModulesAtTypes(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): Resolved | undefined {
724-
return loadModuleFromNodeModulesWorker(Extensions.TypeScript, moduleName, directory, failedLookupLocations, state, /*checkOneLevel*/ false, /*typesOnly*/ true);
717+
// Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly.
718+
return loadModuleFromNodeModulesWorker(Extensions.DtsOnly, moduleName, directory, failedLookupLocations, state, /*typesOnly*/ true);
725719
}
726720

727-
function loadModuleFromNodeModulesWorker(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean, typesOnly: boolean): Resolved | undefined {
728-
directory = normalizeSlashes(directory);
729-
while (true) {
730-
if (getBaseFileName(directory) !== "node_modules") {
731-
const resolved = tryInDirectory();
732-
if (resolved) {
733-
return resolved;
734-
}
735-
}
736-
737-
const parentPath = getDirectoryPath(directory);
738-
if (parentPath === directory || checkOneLevel) {
739-
return undefined;
721+
function loadModuleFromNodeModulesWorker(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, typesOnly: boolean): Resolved | undefined {
722+
return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => {
723+
if (getBaseFileName(ancestorDirectory) !== "node_modules") {
724+
return loadModuleFromNodeModulesOneLevel(extensions, moduleName, ancestorDirectory, failedLookupLocations, state, typesOnly);
740725
}
726+
});
727+
}
741728

742-
directory = parentPath;
729+
/** Load a module from a single node_modules directory, but not from any ancestors' node_modules directories. */
730+
function loadModuleFromNodeModulesOneLevel(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, typesOnly = false): Resolved | undefined {
731+
const packageResult = typesOnly ? undefined : loadModuleFromNodeModulesFolder(extensions, moduleName, directory, failedLookupLocations, state);
732+
if (packageResult) {
733+
return packageResult;
743734
}
744-
745-
function tryInDirectory(): Resolved | undefined {
746-
const packageResult = typesOnly ? undefined : loadModuleFromNodeModulesFolder(extensions, moduleName, directory, failedLookupLocations, state);
747-
return packageResult || loadModuleFromNodeModulesFolder(extensions, combinePaths("@types", moduleName), directory, failedLookupLocations, state);
735+
if (extensions !== Extensions.JavaScript) {
736+
return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, combinePaths("@types", moduleName), directory, failedLookupLocations, state);
748737
}
749738
}
750739

@@ -764,7 +753,11 @@ namespace ts {
764753
}
765754

766755
if (moduleHasNonRelativeName(moduleName)) {
767-
const resolved = loadModuleFromAncestorDirectories(extensions, moduleName, containingDirectory, failedLookupLocations, state);
756+
// Climb up parent directories looking for a module.
757+
const resolved = forEachAncestorDirectory(containingDirectory, directory => {
758+
const searchName = normalizePath(combinePaths(directory, moduleName));
759+
return loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state);
760+
});
768761
if (resolved) {
769762
return resolved;
770763
}
@@ -780,22 +773,6 @@ namespace ts {
780773
}
781774
}
782775

783-
/** Climb up parent directories looking for a module. */
784-
function loadModuleFromAncestorDirectories(extensions: Extensions, moduleName: string, containingDirectory: string, failedLookupLocations: string[], state: ModuleResolutionState): Resolved | undefined {
785-
while (true) {
786-
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
787-
const referencedSourceFile = loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state);
788-
if (referencedSourceFile) {
789-
return referencedSourceFile;
790-
}
791-
const parentPath = getDirectoryPath(containingDirectory);
792-
if (parentPath === containingDirectory) {
793-
return undefined;
794-
}
795-
containingDirectory = parentPath;
796-
}
797-
}
798-
799776
/**
800777
* LSHost may load a module from a global cache of typings.
801778
* This is the minumum code needed to expose that functionality; the rest is in LSHost.
@@ -808,8 +785,24 @@ namespace ts {
808785
}
809786
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled };
810787
const failedLookupLocations: string[] = [];
811-
const resolved = loadModuleFromNodeModules(Extensions.TypeScript, moduleName, globalCache, failedLookupLocations, state, /*checkOneLevel*/ true) ||
812-
loadModuleFromNodeModules(Extensions.JavaScript, moduleName, globalCache, failedLookupLocations, state, /*checkOneLevel*/ true);
788+
const resolved = loadModuleFromNodeModulesOneLevel(Extensions.DtsOnly, moduleName, globalCache, failedLookupLocations, state);
813789
return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ true, failedLookupLocations);
814790
}
791+
792+
/** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
793+
function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T | undefined): T | undefined {
794+
while (true) {
795+
const result = callback(directory);
796+
if (result !== undefined) {
797+
return result;
798+
}
799+
800+
const parentPath = getDirectoryPath(directory);
801+
if (parentPath === directory) {
802+
return undefined;
803+
}
804+
805+
directory = parentPath;
806+
}
807+
}
815808
}

src/harness/unittests/moduleResolution.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,9 @@ namespace ts {
197197
"/a/b/c/d/node_modules/foo/index.tsx",
198198
"/a/b/c/d/node_modules/foo/index.d.ts",
199199

200-
"/a/b/c/d/node_modules/@types/foo.ts",
201-
"/a/b/c/d/node_modules/@types/foo.tsx",
202200
"/a/b/c/d/node_modules/@types/foo.d.ts",
203201
"/a/b/c/d/node_modules/@types/foo/package.json",
204202

205-
"/a/b/c/d/node_modules/@types/foo/index.ts",
206-
"/a/b/c/d/node_modules/@types/foo/index.tsx",
207203
"/a/b/c/d/node_modules/@types/foo/index.d.ts",
208204

209205
"/a/b/c/node_modules/foo.ts",
@@ -215,13 +211,9 @@ namespace ts {
215211
"/a/b/c/node_modules/foo/index.tsx",
216212
"/a/b/c/node_modules/foo/index.d.ts",
217213

218-
"/a/b/c/node_modules/@types/foo.ts",
219-
"/a/b/c/node_modules/@types/foo.tsx",
220214
"/a/b/c/node_modules/@types/foo.d.ts",
221215
"/a/b/c/node_modules/@types/foo/package.json",
222216

223-
"/a/b/c/node_modules/@types/foo/index.ts",
224-
"/a/b/c/node_modules/@types/foo/index.tsx",
225217
"/a/b/c/node_modules/@types/foo/index.d.ts",
226218
]);
227219
}
@@ -257,13 +249,9 @@ namespace ts {
257249
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
258250
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
259251

260-
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.ts",
261-
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.tsx",
262252
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
263253
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/package.json",
264254

265-
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.ts",
266-
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.tsx",
267255
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.d.ts",
268256

269257
"/a/node_modules/b/c/node_modules/foo.ts",
@@ -275,13 +263,9 @@ namespace ts {
275263
"/a/node_modules/b/c/node_modules/foo/index.tsx",
276264
"/a/node_modules/b/c/node_modules/foo/index.d.ts",
277265

278-
"/a/node_modules/b/c/node_modules/@types/foo.ts",
279-
"/a/node_modules/b/c/node_modules/@types/foo.tsx",
280266
"/a/node_modules/b/c/node_modules/@types/foo.d.ts",
281267
"/a/node_modules/b/c/node_modules/@types/foo/package.json",
282268

283-
"/a/node_modules/b/c/node_modules/@types/foo/index.ts",
284-
"/a/node_modules/b/c/node_modules/@types/foo/index.tsx",
285269
"/a/node_modules/b/c/node_modules/@types/foo/index.d.ts",
286270

287271
"/a/node_modules/b/node_modules/foo.ts",
@@ -293,13 +277,9 @@ namespace ts {
293277
"/a/node_modules/b/node_modules/foo/index.tsx",
294278
"/a/node_modules/b/node_modules/foo/index.d.ts",
295279

296-
"/a/node_modules/b/node_modules/@types/foo.ts",
297-
"/a/node_modules/b/node_modules/@types/foo.tsx",
298280
"/a/node_modules/b/node_modules/@types/foo.d.ts",
299281
"/a/node_modules/b/node_modules/@types/foo/package.json",
300282

301-
"/a/node_modules/b/node_modules/@types/foo/index.ts",
302-
"/a/node_modules/b/node_modules/@types/foo/index.tsx",
303283
"/a/node_modules/b/node_modules/@types/foo/index.d.ts",
304284

305285
"/a/node_modules/foo.ts",
@@ -709,13 +689,9 @@ import b = require("./moduleB");
709689
"/root/folder1/node_modules/file6/index.tsx",
710690
"/root/folder1/node_modules/file6/index.d.ts",
711691

712-
"/root/folder1/node_modules/@types/file6.ts",
713-
"/root/folder1/node_modules/@types/file6.tsx",
714692
"/root/folder1/node_modules/@types/file6.d.ts",
715693

716694
"/root/folder1/node_modules/@types/file6/package.json",
717-
"/root/folder1/node_modules/@types/file6/index.ts",
718-
"/root/folder1/node_modules/@types/file6/index.tsx",
719695
"/root/folder1/node_modules/@types/file6/index.d.ts",
720696
// success on /root/node_modules/file6.ts
721697
], /*isExternalLibraryImport*/ true);

0 commit comments

Comments
 (0)