Skip to content

Commit 49ad395

Browse files
committed
resolveModuleName => resolvedModuleNames, added tests
1 parent f22c160 commit 49ad395

File tree

7 files changed

+283
-58
lines changed

7 files changed

+283
-58
lines changed

Jakefile.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ var harnessSources = harnessCoreSources.concat([
142142
"versionCache.ts",
143143
"convertToBase64.ts",
144144
"transpile.ts",
145-
"reuseProgramStructure.ts"
145+
"reuseProgramStructure.ts",
146+
"cachingInServerLSHost.ts"
146147
].map(function (f) {
147148
return path.join(unittestsDirectory, f);
148149
})).concat([

src/compiler/program.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -224,15 +224,17 @@ namespace ts {
224224
host = host || createCompilerHost(options);
225225

226226
// initialize resolveModuleNameWorker only if noResolve is false
227-
let resolveModuleNameWorker: (moduleName: string, containingFile: string) => string;
227+
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => string[];
228228
if (!options.noResolve) {
229-
resolveModuleNameWorker = host.resolveModuleName;
230-
if (!resolveModuleNameWorker) {
229+
resolveModuleNamesWorker = host.resolveModuleNames;
230+
if (!resolveModuleNamesWorker) {
231231
Debug.assert(host.getModuleResolutionHost !== undefined);
232232
let defaultResolver = getDefaultModuleNameResolver(options);
233-
resolveModuleNameWorker = (moduleName, containingFile) => {
234-
let moduleResolution = defaultResolver(moduleName, containingFile, options, host.getModuleResolutionHost());
235-
return moduleResolution.resolvedFileName;
233+
resolveModuleNamesWorker = (moduleNames, containingFile) => {
234+
return map(moduleNames, moduleName => {
235+
let moduleResolution = defaultResolver(moduleName, containingFile, options, host.getModuleResolutionHost());
236+
return moduleResolution.resolvedFileName;
237+
});
236238
}
237239
}
238240
}
@@ -347,15 +349,16 @@ namespace ts {
347349
return false;
348350
}
349351

350-
if (resolveModuleNameWorker) {
352+
if (resolveModuleNamesWorker) {
353+
let moduleNames = map(newSourceFile.imports, name => name.text);
354+
let resolutions = resolveModuleNamesWorker(moduleNames, newSourceFile.fileName);
351355
// ensure that module resolution results are still correct
352-
for (let importName of newSourceFile.imports) {
353-
var oldResolution = getResolvedModuleFileName(oldSourceFile, importName.text);
354-
var newResolution = resolveModuleNameWorker(importName.text, newSourceFile.fileName);
355-
if (oldResolution !== newResolution) {
356+
for (let i = 0; i < moduleNames.length; ++i) {
357+
let oldResolution = getResolvedModuleFileName(oldSourceFile, moduleNames[i]);
358+
if (oldResolution !== resolutions[i]) {
356359
return false;
357-
}
358-
}
360+
}
361+
}
359362
}
360363
// pass the cache of module resolutions from the old source file
361364
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
@@ -719,10 +722,16 @@ namespace ts {
719722
if (file.imports.length) {
720723
file.resolvedModules = {};
721724
let oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
722-
for (let moduleName of file.imports) {
723-
resolveModule(moduleName);
724-
}
725725

726+
let moduleNames = map(file.imports, name => name.text);
727+
let resolutions = resolveModuleNamesWorker(moduleNames, file.fileName);
728+
for (let i = 0; i < file.imports.length; ++i) {
729+
let resolution = resolutions[i];
730+
setResolvedModuleName(file, moduleNames[i], resolution);
731+
if (resolution) {
732+
findModuleSourceFile(resolution, file.imports[i]);
733+
}
734+
}
726735
}
727736
else {
728737
// no imports - drop cached module resolutions
@@ -733,16 +742,6 @@ namespace ts {
733742
function findModuleSourceFile(fileName: string, nameLiteral: Expression) {
734743
return findSourceFile(fileName, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos);
735744
}
736-
737-
function resolveModule(moduleNameExpr: LiteralExpression): void {
738-
Debug.assert(resolveModuleNameWorker !== undefined);
739-
740-
let resolvedModuleName = resolveModuleNameWorker(moduleNameExpr.text, file.fileName);
741-
setResolvedModuleName(file, moduleNameExpr.text, resolvedModuleName);
742-
if (resolvedModuleName) {
743-
findModuleSourceFile(resolvedModuleName, moduleNameExpr);
744-
}
745-
}
746745
}
747746

748747
function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2266,7 +2266,7 @@ namespace ts {
22662266
// if getModuleResolutionHost is implemented then compiler will apply one of built-in ways to resolve module names
22672267
// and ModuleResolutionHost will be used to ask host specific questions
22682268
// if resolveModuleName is implemented - this will mean that host is completely in charge of module name resolution
2269-
resolveModuleName?(moduleName: string, containingFile: string): string;
2269+
resolveModuleNames?(moduleNames: string[], containingFile: string): string[];
22702270
getModuleResolutionHost?(): ModuleResolutionHost;
22712271
}
22722272

src/server/editorServices.ts

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,30 +98,54 @@ namespace ts.server {
9898
}
9999
}
100100

101-
resolveModuleName(moduleName: string, containingFile: string): string {
102-
let resolutionsInFile = this.resolvedModuleNames.get(containingFile);
103-
if (!resolutionsInFile) {
104-
resolutionsInFile = {};
105-
this.resolvedModuleNames.set(containingFile, resolutionsInFile);
101+
resolveModuleNames(moduleNames: string[], containingFile: string): string[] {
102+
let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile);
103+
104+
let newResolutions: Map<TimestampedResolvedModule> = {};
105+
let resolvedFileNames: string[] = [];
106+
107+
let compilerOptions = this.getCompilationSettings();
108+
let defaultResolver = ts.getDefaultModuleNameResolver(compilerOptions);
109+
110+
for (let moduleName of moduleNames) {
111+
// check if this is a duplicate entry in the list
112+
let resolution = lookUp(newResolutions, moduleName);
113+
if (!resolution) {
114+
let existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, moduleName);
115+
if (moduleResolutionIsValid(existingResolution)) {
116+
// ok, it is safe to use existing module resolution results
117+
resolution = existingResolution;
118+
}
119+
else {
120+
resolution = <TimestampedResolvedModule>defaultResolver(moduleName, containingFile, compilerOptions, this.moduleResolutionHost);
121+
resolution.lastCheckTime = Date.now();
122+
newResolutions[moduleName] = resolution;
123+
}
124+
}
125+
126+
ts.Debug.assert(resolution !== undefined);
127+
128+
resolvedFileNames.push(resolution.resolvedFileName);
106129
}
107130

108-
let resolution = ts.lookUp(resolutionsInFile, moduleName);
109-
if (!moduleResolutionIsValid(resolution)) {
110-
let compilerOptions = this.getCompilationSettings();
111-
let defaultResolver = ts.getDefaultModuleNameResolver(compilerOptions);
112-
resolution = <TimestampedResolvedModule>defaultResolver(moduleName, containingFile, compilerOptions, this.moduleResolutionHost);
113-
resolution.lastCheckTime = Date.now();
114-
resolutionsInFile[moduleName] = resolution;
115-
}
116-
return resolution.resolvedFileName;
131+
// replace old results with a new one
132+
this.resolvedModuleNames.set(containingFile, newResolutions);
133+
return resolvedFileNames;
117134

118135
function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean {
119136
if (!resolution) {
120137
return false;
121138
}
122139

123-
// TODO: use lastCheckTime assuming that module resolution results are legal for some period of time
124-
return !resolution.resolvedFileName && resolution.failedLookupLocations.length !== 0;
140+
if (resolution.resolvedFileName) {
141+
// TODO: consider checking failedLookupLocations
142+
// TODO: use lastCheckTime to track expiration for module name resolution
143+
return true;
144+
}
145+
146+
// consider situation if we have no candidate locations as valid resolution.
147+
// after all there is no point to invalidate it if we have no idea where to look for the module.
148+
return resolution.failedLookupLocations.length === 0;
125149
}
126150
}
127151

src/services/services.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ namespace ts {
983983
// if resolveModuleName is implemented - this will mean that host is completely in charge of module name resolution
984984
// if none of these methods are implemented then language service will try to emulate getModuleResolutionHost atop of 'getScriptSnapshot'
985985
getModuleResolutionHost?(): ModuleResolutionHost;
986-
resolveModuleName?(moduleName: string, containingFile: string): string;
986+
resolveModuleNames?(moduleNames: string[], containingFile: string): string[];
987987
}
988988

989989
//
@@ -2564,7 +2564,8 @@ namespace ts {
25642564

25652565
let oldSettings = program && program.getCompilerOptions();
25662566
let newSettings = hostCache.compilationSettings();
2567-
let changesInCompilationSettingsAffectSyntax = oldSettings && oldSettings.target !== newSettings.target;
2567+
let changesInCompilationSettingsAffectSyntax = oldSettings &&
2568+
(oldSettings.target !== newSettings.target || oldSettings.module !== newSettings.module || oldSettings.noResolve !== newSettings.noResolve);
25682569

25692570
// Now create a new compiler
25702571
let compilerHost: CompilerHost = {
@@ -2578,8 +2579,8 @@ namespace ts {
25782579
getCurrentDirectory: () => host.getCurrentDirectory(),
25792580
};
25802581

2581-
if (host.resolveModuleName) {
2582-
compilerHost.resolveModuleName = (moduleName, containingFile) => host.resolveModuleName(moduleName, containingFile)
2582+
if (host.resolveModuleNames) {
2583+
compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile)
25832584
}
25842585
else if (host.getModuleResolutionHost) {
25852586
compilerHost.getModuleResolutionHost = () => host.getModuleResolutionHost()

src/services/shims.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -267,22 +267,16 @@ namespace ts {
267267
private files: string[];
268268
private loggingEnabled = false;
269269
private tracingEnabled = false;
270-
private lastRequestedFile: string;
271-
private lastRequestedModuleResolutions: Map<string>;
272270

273-
public resolveModuleName: (moduleName: string, containingFile: string) => string;
271+
public resolveModuleNames: (moduleName: string[], containingFile: string) => string[];
274272

275273
constructor(private shimHost: LanguageServiceShimHost) {
276274
// if shimHost is a COM object then property check will become method call with no arguments.
277275
// 'in' does not have this effect.
278276
if ("getModuleResolutionsForFile" in this.shimHost) {
279-
this.resolveModuleName = (moduleName: string, containingFile: string) => {
280-
if (this.lastRequestedFile !== containingFile) {
281-
this.lastRequestedModuleResolutions = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
282-
this.lastRequestedFile = containingFile;
283-
}
284-
285-
return this.lastRequestedModuleResolutions[moduleName];
277+
this.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
278+
let resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
279+
return map(moduleNames, name => lookUp(resolutionsInFile, name));
286280
};
287281
}
288282
}

0 commit comments

Comments
 (0)