Skip to content

Commit a861913

Browse files
committed
Merge pull request #6378 from Microsoft/directoryExists
add optional 'directoryExists' method to hosts to reduce amount of di…
2 parents b168da9 + 36af815 commit a861913

File tree

7 files changed

+222
-110
lines changed

7 files changed

+222
-110
lines changed

src/compiler/program.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ namespace ts {
5353
if (getRootLength(moduleName) !== 0 || nameStartsWithDotSlashOrDotDotSlash(moduleName)) {
5454
const failedLookupLocations: string[] = [];
5555
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
56-
let resolvedFileName = loadNodeModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host);
56+
let resolvedFileName = loadNodeModuleFromFile(supportedExtensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, host);
5757

5858
if (resolvedFileName) {
5959
return { resolvedModule: { resolvedFileName }, failedLookupLocations };
6060
}
6161

62-
resolvedFileName = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, host);
62+
resolvedFileName = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, host);
6363
return resolvedFileName
6464
? { resolvedModule: { resolvedFileName }, failedLookupLocations }
6565
: { resolvedModule: undefined, failedLookupLocations };
@@ -69,12 +69,22 @@ namespace ts {
6969
}
7070
}
7171

72-
function loadNodeModuleFromFile(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string {
72+
/* @internal */
73+
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean } ): boolean {
74+
// if host does not support 'directoryExists' assume that directory will exist
75+
return !host.directoryExists || host.directoryExists(directoryName);
76+
}
77+
78+
/**
79+
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
80+
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
81+
*/
82+
function loadNodeModuleFromFile(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, host: ModuleResolutionHost): string {
7383
return forEach(extensions, tryLoad);
7484

7585
function tryLoad(ext: string): string {
7686
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
77-
if (host.fileExists(fileName)) {
87+
if (!onlyRecordFailures && host.fileExists(fileName)) {
7888
return fileName;
7989
}
8090
else {
@@ -84,9 +94,10 @@ namespace ts {
8494
}
8595
}
8696

87-
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string {
97+
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, host: ModuleResolutionHost): string {
8898
const packageJsonPath = combinePaths(candidate, "package.json");
89-
if (host.fileExists(packageJsonPath)) {
99+
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, host);
100+
if (directoryExists && host.fileExists(packageJsonPath)) {
90101

91102
let jsonContent: { typings?: string };
92103

@@ -100,7 +111,8 @@ namespace ts {
100111
}
101112

102113
if (typeof jsonContent.typings === "string") {
103-
const result = loadNodeModuleFromFile(extensions, normalizePath(combinePaths(candidate, jsonContent.typings)), failedLookupLocation, host);
114+
const path = normalizePath(combinePaths(candidate, jsonContent.typings));
115+
const result = loadNodeModuleFromFile(extensions, path, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(path), host), host);
104116
if (result) {
105117
return result;
106118
}
@@ -111,7 +123,7 @@ namespace ts {
111123
failedLookupLocation.push(packageJsonPath);
112124
}
113125

114-
return loadNodeModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, host);
126+
return loadNodeModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, !directoryExists, host);
115127
}
116128

117129
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
@@ -121,14 +133,15 @@ namespace ts {
121133
const baseName = getBaseFileName(directory);
122134
if (baseName !== "node_modules") {
123135
const nodeModulesFolder = combinePaths(directory, "node_modules");
136+
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, host);
124137
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
125138
// Load only typescript files irrespective of allowJs option if loading from node modules
126-
let result = loadNodeModuleFromFile(supportedTypeScriptExtensions, candidate, failedLookupLocations, host);
139+
let result = loadNodeModuleFromFile(supportedTypeScriptExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, host);
127140
if (result) {
128141
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
129142
}
130143

131-
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, host);
144+
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, host);
132145
if (result) {
133146
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
134147
}
@@ -281,7 +294,8 @@ namespace ts {
281294
getCanonicalFileName,
282295
getNewLine: () => newLine,
283296
fileExists: fileName => sys.fileExists(fileName),
284-
readFile: fileName => sys.readFile(fileName)
297+
readFile: fileName => sys.readFile(fileName),
298+
directoryExists: directoryName => sys.directoryExists(directoryName)
285299
};
286300
}
287301

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2649,6 +2649,8 @@ namespace ts {
26492649
// readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json'
26502650
// to determine location of bundled typings for node module
26512651
readFile(fileName: string): string;
2652+
2653+
directoryExists?(directoryName: string): boolean;
26522654
}
26532655

26542656
export interface ResolvedModule {

src/harness/harnessLanguageService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ namespace Harness.LanguageService {
267267
log(s: string): void { this.nativeHost.log(s); }
268268
trace(s: string): void { this.nativeHost.trace(s); }
269269
error(s: string): void { this.nativeHost.error(s); }
270+
directoryExists(directoryName: string): boolean {
271+
// for tests pessimistically assume that directory always exists
272+
return true;
273+
}
270274
}
271275

272276
class ClassifierShimProxy implements ts.Classifier {

src/server/editorServices.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ namespace ts.server {
100100
this.filenameToScript = createFileMap<ScriptInfo>();
101101
this.moduleResolutionHost = {
102102
fileExists: fileName => this.fileExists(fileName),
103-
readFile: fileName => this.host.readFile(fileName)
103+
readFile: fileName => this.host.readFile(fileName),
104+
directoryExists: directoryName => this.host.directoryExists(directoryName)
104105
};
105106
}
106107

src/services/services.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ namespace ts {
10341034
* host specific questions using 'getScriptSnapshot'.
10351035
*/
10361036
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
1037+
directoryExists?(directoryName: string): boolean;
10371038
}
10381039

10391040
//
@@ -1911,7 +1912,8 @@ namespace ts {
19111912
getCurrentDirectory: () => "",
19121913
getNewLine: () => newLine,
19131914
fileExists: (fileName): boolean => fileName === inputFileName,
1914-
readFile: (fileName): string => ""
1915+
readFile: (fileName): string => "",
1916+
directoryExists: directoryExists => true
19151917
};
19161918

19171919
const program = createProgram([inputFileName], options, compilerHost);
@@ -2768,6 +2770,10 @@ namespace ts {
27682770
// stub missing host functionality
27692771
const entry = hostCache.getOrCreateEntry(fileName);
27702772
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
2773+
},
2774+
directoryExists: directoryName => {
2775+
Debug.assert(!host.resolveModuleNames);
2776+
return directoryProbablyExists(directoryName, host);
27712777
}
27722778
};
27732779

src/services/shims.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ namespace ts {
6262
useCaseSensitiveFileNames?(): boolean;
6363

6464
getModuleResolutionsForFile?(fileName: string): string;
65+
directoryExists(directoryName: string): boolean;
6566
}
6667

6768
/** Public interface of the the of a config service shim instance.*/
@@ -274,6 +275,7 @@ namespace ts {
274275
private tracingEnabled = false;
275276

276277
public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModule[];
278+
public directoryExists: (directoryName: string) => boolean;
277279

278280
constructor(private shimHost: LanguageServiceShimHost) {
279281
// if shimHost is a COM object then property check will become method call with no arguments.
@@ -287,6 +289,9 @@ namespace ts {
287289
});
288290
};
289291
}
292+
if ("directoryExists" in this.shimHost) {
293+
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
294+
}
290295
}
291296

292297
public log(s: string): void {
@@ -405,9 +410,14 @@ namespace ts {
405410
}
406411
}
407412

408-
export class CoreServicesShimHostAdapter implements ParseConfigHost {
413+
export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost {
409414

415+
public directoryExists: (directoryName: string) => boolean;
416+
410417
constructor(private shimHost: CoreServicesShimHost) {
418+
if ("directoryExists" in this.shimHost) {
419+
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
420+
}
411421
}
412422

413423
public readDirectory(rootDir: string, extension: string, exclude: string[]): string[] {
@@ -424,11 +434,11 @@ namespace ts {
424434
}
425435
return JSON.parse(encoded);
426436
}
427-
437+
428438
public fileExists(fileName: string): boolean {
429439
return this.shimHost.fileExists(fileName);
430440
}
431-
441+
432442
public readFile(fileName: string): string {
433443
return this.shimHost.readFile(fileName);
434444
}

0 commit comments

Comments
 (0)