Skip to content

Commit 38ce627

Browse files
committed
add tsserver specific resolution pass that will load typings from cache locations if auto discovery is enabled
1 parent ce02f83 commit 38ce627

10 files changed

Lines changed: 223 additions & 62 deletions

File tree

src/compiler/core.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,7 +1571,8 @@ namespace ts {
15711571
return true;
15721572
}
15731573

1574-
function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
1574+
/* @internal */
1575+
export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
15751576
return { resolvedModule: resolvedFileName ? { resolvedFileName, isExternalLibraryImport } : undefined, failedLookupLocations };
15761577
}
15771578

@@ -1989,7 +1990,7 @@ namespace ts {
19891990
if (traceEnabled) {
19901991
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
19911992
}
1992-
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state);
1993+
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state, /*checkOneLevel*/ false);
19931994
isExternalLibraryImport = resolvedFileName !== undefined;
19941995
}
19951996
else {
@@ -2138,7 +2139,7 @@ namespace ts {
21382139
}
21392140

21402141
/* @internal */
2141-
export function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
2142+
export function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean): string {
21422143
directory = normalizeSlashes(directory);
21432144
while (true) {
21442145
const baseName = getBaseFileName(directory);
@@ -2159,7 +2160,7 @@ namespace ts {
21592160
}
21602161

21612162
const parentPath = getDirectoryPath(directory);
2162-
if (parentPath === directory) {
2163+
if (parentPath === directory || checkOneLevel) {
21632164
break;
21642165
}
21652166

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,6 +2840,10 @@
28402840
"category": "Error",
28412841
"code": 6138
28422842
},
2843+
"Auto discovery for typings is enabled in project '{0}'. Running extra resolution pass for module '{1}' using cache location '{2}'.": {
2844+
"category": "Error",
2845+
"code": 6139
2846+
},
28432847
"Variable '{0}' implicitly has an '{1}' type.": {
28442848
"category": "Error",
28452849
"code": 7005

src/compiler/program.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ namespace ts {
174174
if (traceEnabled) {
175175
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
176176
}
177-
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState);
177+
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState, /*checkOneLevel*/ false);
178178
if (traceEnabled) {
179179
if (resolvedFile) {
180180
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ namespace ts {
3030

3131
class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
3232
protected projectService: server.ProjectService;
33-
constructor(readonly cachePath: string, readonly installTypingHost: server.ServerHost) {
34-
super(cachePath, <Path>"");
33+
constructor(readonly globalTypingsCacheLocation: string, readonly installTypingHost: server.ServerHost) {
34+
super(globalTypingsCacheLocation, <Path>"");
3535
this.init();
3636
}
3737

@@ -75,7 +75,7 @@ namespace ts {
7575
}
7676

7777
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
78-
const request = server.createInstallTypingsRequest(project, typingOptions, this.cachePath);
78+
const request = server.createInstallTypingsRequest(project, typingOptions, this.globalTypingsCacheLocation);
7979
this.install(request);
8080
}
8181
}
@@ -2030,7 +2030,7 @@ namespace ts {
20302030
const p = projectService.configuredProjects[0];
20312031
checkProjectActualFiles(p, [file1.path]);
20322032

2033-
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
2033+
assert(host.fileExists(combinePaths(installer.globalTypingsCacheLocation, "tsd.json")));
20342034

20352035
installer.runPostInstallActions(t => {
20362036
assert.deepEqual(t, ["jquery"]);
@@ -2070,7 +2070,7 @@ namespace ts {
20702070
const p = projectService.inferredProjects[0];
20712071
checkProjectActualFiles(p, [file1.path]);
20722072

2073-
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
2073+
assert(host.fileExists(combinePaths(installer.globalTypingsCacheLocation, "tsd.json")));
20742074

20752075
installer.runPostInstallActions(t => {
20762076
assert.deepEqual(t, ["jquery"]);
@@ -2398,4 +2398,121 @@ namespace ts {
23982398
assert.isTrue(typingOptions.enableAutoDiscovery, "Typing autodiscovery should be enabled");
23992399
});
24002400
});
2401+
2402+
describe("extra resolution pass in lshost", () => {
2403+
it("can load typings that are proper modules", () => {
2404+
const file1 = {
2405+
path: "/a/b/app.js",
2406+
content: `var x = require("lib")`
2407+
};
2408+
const lib = {
2409+
path: "/a/cache/node_modules/@types/lib/index.d.ts",
2410+
content: "export let x = 1"
2411+
};
2412+
const host: TestServerHost & ModuleResolutionHost = createServerHost([file1, lib]);
2413+
const resolutionTrace: string[] = [];
2414+
host.trace = resolutionTrace.push.bind(resolutionTrace);
2415+
const projectService = createProjectService(host, { typingsInstaller: new TestTypingsInstaller("/a/cache", host) });
2416+
2417+
projectService.setCompilerOptionsForInferredProjects({ traceResolution: true, allowJs: true });
2418+
projectService.openClientFile(file1.path);
2419+
projectService.checkNumberOfProjects({ inferredProjects: 1 });
2420+
const proj = projectService.inferredProjects[0];
2421+
2422+
assert.deepEqual(resolutionTrace, [
2423+
"======== Resolving module 'lib' from '/a/b/app.js'. ========",
2424+
"Module resolution kind is not specified, using 'NodeJs'.",
2425+
"Loading module 'lib' from 'node_modules' folder.",
2426+
"File '/a/b/node_modules/lib.ts' does not exist.",
2427+
"File '/a/b/node_modules/lib.tsx' does not exist.",
2428+
"File '/a/b/node_modules/lib.d.ts' does not exist.",
2429+
"File '/a/b/node_modules/lib.js' does not exist.",
2430+
"File '/a/b/node_modules/lib.jsx' does not exist.",
2431+
"File '/a/b/node_modules/lib/package.json' does not exist.",
2432+
"File '/a/b/node_modules/lib/index.ts' does not exist.",
2433+
"File '/a/b/node_modules/lib/index.tsx' does not exist.",
2434+
"File '/a/b/node_modules/lib/index.d.ts' does not exist.",
2435+
"File '/a/b/node_modules/lib/index.js' does not exist.",
2436+
"File '/a/b/node_modules/lib/index.jsx' does not exist.",
2437+
"File '/a/b/node_modules/@types/lib.ts' does not exist.",
2438+
"File '/a/b/node_modules/@types/lib.tsx' does not exist.",
2439+
"File '/a/b/node_modules/@types/lib.d.ts' does not exist.",
2440+
"File '/a/b/node_modules/@types/lib.js' does not exist.",
2441+
"File '/a/b/node_modules/@types/lib.jsx' does not exist.",
2442+
"File '/a/b/node_modules/@types/lib/package.json' does not exist.",
2443+
"File '/a/b/node_modules/@types/lib/index.ts' does not exist.",
2444+
"File '/a/b/node_modules/@types/lib/index.tsx' does not exist.",
2445+
"File '/a/b/node_modules/@types/lib/index.d.ts' does not exist.",
2446+
"File '/a/b/node_modules/@types/lib/index.js' does not exist.",
2447+
"File '/a/b/node_modules/@types/lib/index.jsx' does not exist.",
2448+
"File '/a/node_modules/lib.ts' does not exist.",
2449+
"File '/a/node_modules/lib.tsx' does not exist.",
2450+
"File '/a/node_modules/lib.d.ts' does not exist.",
2451+
"File '/a/node_modules/lib.js' does not exist.",
2452+
"File '/a/node_modules/lib.jsx' does not exist.",
2453+
"File '/a/node_modules/lib/package.json' does not exist.",
2454+
"File '/a/node_modules/lib/index.ts' does not exist.",
2455+
"File '/a/node_modules/lib/index.tsx' does not exist.",
2456+
"File '/a/node_modules/lib/index.d.ts' does not exist.",
2457+
"File '/a/node_modules/lib/index.js' does not exist.",
2458+
"File '/a/node_modules/lib/index.jsx' does not exist.",
2459+
"File '/a/node_modules/@types/lib.ts' does not exist.",
2460+
"File '/a/node_modules/@types/lib.tsx' does not exist.",
2461+
"File '/a/node_modules/@types/lib.d.ts' does not exist.",
2462+
"File '/a/node_modules/@types/lib.js' does not exist.",
2463+
"File '/a/node_modules/@types/lib.jsx' does not exist.",
2464+
"File '/a/node_modules/@types/lib/package.json' does not exist.",
2465+
"File '/a/node_modules/@types/lib/index.ts' does not exist.",
2466+
"File '/a/node_modules/@types/lib/index.tsx' does not exist.",
2467+
"File '/a/node_modules/@types/lib/index.d.ts' does not exist.",
2468+
"File '/a/node_modules/@types/lib/index.js' does not exist.",
2469+
"File '/a/node_modules/@types/lib/index.jsx' does not exist.",
2470+
"File '/node_modules/lib.ts' does not exist.",
2471+
"File '/node_modules/lib.tsx' does not exist.",
2472+
"File '/node_modules/lib.d.ts' does not exist.",
2473+
"File '/node_modules/lib.js' does not exist.",
2474+
"File '/node_modules/lib.jsx' does not exist.",
2475+
"File '/node_modules/lib/package.json' does not exist.",
2476+
"File '/node_modules/lib/index.ts' does not exist.",
2477+
"File '/node_modules/lib/index.tsx' does not exist.",
2478+
"File '/node_modules/lib/index.d.ts' does not exist.",
2479+
"File '/node_modules/lib/index.js' does not exist.",
2480+
"File '/node_modules/lib/index.jsx' does not exist.",
2481+
"File '/node_modules/@types/lib.ts' does not exist.",
2482+
"File '/node_modules/@types/lib.tsx' does not exist.",
2483+
"File '/node_modules/@types/lib.d.ts' does not exist.",
2484+
"File '/node_modules/@types/lib.js' does not exist.",
2485+
"File '/node_modules/@types/lib.jsx' does not exist.",
2486+
"File '/node_modules/@types/lib/package.json' does not exist.",
2487+
"File '/node_modules/@types/lib/index.ts' does not exist.",
2488+
"File '/node_modules/@types/lib/index.tsx' does not exist.",
2489+
"File '/node_modules/@types/lib/index.d.ts' does not exist.",
2490+
"File '/node_modules/@types/lib/index.js' does not exist.",
2491+
"File '/node_modules/@types/lib/index.jsx' does not exist.",
2492+
"======== Module name 'lib' was not resolved. ========",
2493+
`Auto discovery for typings is enabled in project '${proj.getProjectName()}'. Running extra resolution pass for module 'lib' using cache location '/a/cache'.`,
2494+
"File '/a/cache/node_modules/lib.ts' does not exist.",
2495+
"File '/a/cache/node_modules/lib.tsx' does not exist.",
2496+
"File '/a/cache/node_modules/lib.d.ts' does not exist.",
2497+
"File '/a/cache/node_modules/lib.js' does not exist.",
2498+
"File '/a/cache/node_modules/lib.jsx' does not exist.",
2499+
"File '/a/cache/node_modules/lib/package.json' does not exist.",
2500+
"File '/a/cache/node_modules/lib/index.ts' does not exist.",
2501+
"File '/a/cache/node_modules/lib/index.tsx' does not exist.",
2502+
"File '/a/cache/node_modules/lib/index.d.ts' does not exist.",
2503+
"File '/a/cache/node_modules/lib/index.js' does not exist.",
2504+
"File '/a/cache/node_modules/lib/index.jsx' does not exist.",
2505+
"File '/a/cache/node_modules/@types/lib.ts' does not exist.",
2506+
"File '/a/cache/node_modules/@types/lib.tsx' does not exist.",
2507+
"File '/a/cache/node_modules/@types/lib.d.ts' does not exist.",
2508+
"File '/a/cache/node_modules/@types/lib.js' does not exist.",
2509+
"File '/a/cache/node_modules/@types/lib.jsx' does not exist.",
2510+
"File '/a/cache/node_modules/@types/lib/package.json' does not exist.",
2511+
"File '/a/cache/node_modules/@types/lib/index.ts' does not exist.",
2512+
"File '/a/cache/node_modules/@types/lib/index.tsx' does not exist.",
2513+
"File '/a/cache/node_modules/@types/lib/index.d.ts' exist - use it as a name resolution result.",
2514+
]);
2515+
checkProjectActualFiles(proj, [ file1.path, lib.path ]);
2516+
});
2517+
});
24012518
}

src/server/editorServices.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,16 @@ namespace ts.server {
183183
public readonly logger: Logger,
184184
public readonly cancellationToken: HostCancellationToken,
185185
private readonly useSingleInferredProject: boolean,
186-
private typingsInstaller: ITypingsInstaller,
186+
readonly typingsInstaller: ITypingsInstaller = nullTypingsInstaller,
187187
private readonly eventHandler?: ProjectServiceEventHandler) {
188188

189189
this.toCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
190190
this.directoryWatchers = new DirectoryWatchers(this);
191191
this.throttledOperations = new ThrottledOperations(host);
192192

193-
const installer = typingsInstaller || nullTypingsInstaller;
194-
installer.attach(this);
193+
this.typingsInstaller.attach(this);
195194

196-
this.typingsCache = new TypingsCache(installer);
195+
this.typingsCache = new TypingsCache(this.typingsInstaller);
197196

198197
// ts.disableIncrementalParsing = true;
199198

src/server/lsHost.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,37 @@ namespace ts.server {
99
private readonly resolvedTypeReferenceDirectives: ts.FileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>;
1010
private readonly getCanonicalFileName: (fileName: string) => string;
1111

12+
private readonly resolveModuleName: typeof resolveModuleName;
13+
readonly trace: (s: string) => void;
14+
1215
constructor(private readonly host: ServerHost, private readonly project: Project, private readonly cancellationToken: HostCancellationToken) {
1316
this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
1417
this.resolvedModuleNames = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
1518
this.resolvedTypeReferenceDirectives = createFileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
19+
20+
if (host.trace) {
21+
this.trace = s => host.trace(s);
22+
}
23+
24+
this.resolveModuleName = (moduleName, containingFile, compilerOptions, host) => {
25+
const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host);
26+
if (primaryResult.resolvedModule) {
27+
return primaryResult;
28+
}
29+
const globalCache = this.project.projectService.typingsInstaller.globalTypingsCacheLocation;
30+
if (this.project.getTypingOptions().enableAutoDiscovery && globalCache) {
31+
const traceEnabled = isTraceEnabled(compilerOptions, host);
32+
if (traceEnabled) {
33+
trace(host, Diagnostics.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2, this.project.getProjectName(), moduleName, globalCache);
34+
}
35+
const state: ModuleResolutionState = { compilerOptions, host, skipTsx: false, traceEnabled };
36+
const resolvedName = loadModuleFromNodeModules(moduleName, globalCache, primaryResult.failedLookupLocations, state, /*checkOneLevel*/ true);
37+
if (resolvedName) {
38+
return createResolvedModule(resolvedName, /*isExternalLibraryImport*/ true, primaryResult.failedLookupLocations);
39+
}
40+
}
41+
return primaryResult;
42+
};
1643
}
1744

1845
private resolveNamesWithLocalCache<T extends { failedLookupLocations: string[] }, R>(
@@ -89,7 +116,7 @@ namespace ts.server {
89116
}
90117

91118
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] {
92-
return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, resolveModuleName, m => m.resolvedModule);
119+
return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName, m => m.resolvedModule);
93120
}
94121

95122
getDefaultLibFileName() {

0 commit comments

Comments
 (0)