cache per-folder module resolutions during construction of the program (#13030)

This commit is contained in:
Vladimir Matveev 2016-12-19 13:48:45 -08:00 committed by GitHub
parent c05b73328c
commit 0649c2272c
10 changed files with 175 additions and 23 deletions

View file

@ -2945,6 +2945,10 @@
"category": "Message",
"code": 6146
},
"Resolution for module '{0}' was found in cache": {
"category": "Message",
"code": 6147
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005

View file

@ -293,33 +293,69 @@ namespace ts {
return result;
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
/**
* Cached module resolutions per containing directory.
* This assumes that any module id will have the same resolution for sibling files located in the same folder.
*/
export interface ModuleResolutionCache {
getOrCreateCacheForDirectory(directoryName: string): Map<ResolvedModuleWithFailedLookupLocations>;
}
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string) {
const map = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
return { getOrCreateCacheForDirectory };
function getOrCreateCacheForDirectory(directoryName: string) {
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
let perFolderCache = map.get(path);
if (!perFolderCache) {
perFolderCache = createMap<ResolvedModuleWithFailedLookupLocations>();
map.set(path, perFolderCache);
}
return perFolderCache;
}
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let perFolderCache = cache && cache.getOrCreateCacheForDirectory(getDirectoryPath(containingFile));
let result = perFolderCache && perFolderCache[moduleName];
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
if (result) {
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
}
let result: ResolvedModuleWithFailedLookupLocations;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (perFolderCache) {
perFolderCache[moduleName] = result;
}
}
if (traceEnabled) {

View file

@ -325,6 +325,7 @@ namespace ts {
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
let moduleResolutionCache: ModuleResolutionCache;
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
@ -338,7 +339,8 @@ namespace ts {
});
}
else {
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host).resolvedModule;
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule;
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
}
@ -391,6 +393,9 @@ namespace ts {
}
}
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
moduleResolutionCache = undefined;
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined;

View file

@ -0,0 +1,27 @@
//// [tests/cases/compiler/cacheResolutions.ts] ////
//// [app.ts]
export let x = 1;
//// [lib1.ts]
export let x = 1;
//// [lib2.ts]
export let x = 1;
//// [app.js]
define(["require", "exports"], function (require, exports) {
"use strict";
exports.x = 1;
});
//// [lib1.js]
define(["require", "exports"], function (require, exports) {
"use strict";
exports.x = 1;
});
//// [lib2.js]
define(["require", "exports"], function (require, exports) {
"use strict";
exports.x = 1;
});

View file

@ -0,0 +1,13 @@
=== /a/b/c/app.ts ===
export let x = 1;
>x : Symbol(x, Decl(app.ts, 1, 10))
=== /a/b/c/lib1.ts ===
export let x = 1;
>x : Symbol(x, Decl(lib1.ts, 0, 10))
=== /a/b/c/lib2.ts ===
export let x = 1;
>x : Symbol(x, Decl(lib2.ts, 0, 10))

View file

@ -0,0 +1,43 @@
[
"======== Resolving module 'tslib' from '/a/b/c/app.ts'. ========",
"Module resolution kind is not specified, using 'Classic'.",
"File '/a/b/c/tslib.ts' does not exist.",
"File '/a/b/c/tslib.tsx' does not exist.",
"File '/a/b/c/tslib.d.ts' does not exist.",
"File '/a/b/tslib.ts' does not exist.",
"File '/a/b/tslib.tsx' does not exist.",
"File '/a/b/tslib.d.ts' does not exist.",
"File '/a/tslib.ts' does not exist.",
"File '/a/tslib.tsx' does not exist.",
"File '/a/tslib.d.ts' does not exist.",
"File '/tslib.ts' does not exist.",
"File '/tslib.tsx' does not exist.",
"File '/tslib.d.ts' does not exist.",
"File '/a/b/c/node_modules/@types/tslib.d.ts' does not exist.",
"File '/a/b/c/node_modules/@types/tslib/package.json' does not exist.",
"File '/a/b/c/node_modules/@types/tslib/index.d.ts' does not exist.",
"File '/a/b/node_modules/@types/tslib.d.ts' does not exist.",
"File '/a/b/node_modules/@types/tslib/package.json' does not exist.",
"File '/a/b/node_modules/@types/tslib/index.d.ts' does not exist.",
"File '/a/node_modules/@types/tslib.d.ts' does not exist.",
"File '/a/node_modules/@types/tslib/package.json' does not exist.",
"File '/a/node_modules/@types/tslib/index.d.ts' does not exist.",
"File '/node_modules/@types/tslib.d.ts' does not exist.",
"File '/node_modules/@types/tslib/package.json' does not exist.",
"File '/node_modules/@types/tslib/index.d.ts' does not exist.",
"File '/a/b/c/tslib.js' does not exist.",
"File '/a/b/c/tslib.jsx' does not exist.",
"File '/a/b/tslib.js' does not exist.",
"File '/a/b/tslib.jsx' does not exist.",
"File '/a/tslib.js' does not exist.",
"File '/a/tslib.jsx' does not exist.",
"File '/tslib.js' does not exist.",
"File '/tslib.jsx' does not exist.",
"======== Module name 'tslib' was not resolved. ========",
"======== Resolving module 'tslib' from '/a/b/c/lib1.ts'. ========",
"Resolution for module 'tslib' was found in cache",
"======== Module name 'tslib' was not resolved. ========",
"======== Resolving module 'tslib' from '/a/b/c/lib2.ts'. ========",
"Resolution for module 'tslib' was found in cache",
"======== Module name 'tslib' was not resolved. ========"
]

View file

@ -0,0 +1,16 @@
=== /a/b/c/app.ts ===
export let x = 1;
>x : number
>1 : 1
=== /a/b/c/lib1.ts ===
export let x = 1;
>x : number
>1 : 1
=== /a/b/c/lib2.ts ===
export let x = 1;
>x : number
>1 : 1

View file

@ -16,9 +16,7 @@
"Resolving real path for '/types/lib/index.d.ts', result '/types/lib/index.d.ts'",
"======== Type reference directive 'lib' was successfully resolved to '/types/lib/index.d.ts', primary: true. ========",
"======== Resolving module './main' from '/mod1.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module as file / folder, candidate module location '/main'.",
"File '/main.ts' exist - use it as a name resolution result.",
"Resolution for module './main' was found in cache",
"======== Module name './main' was successfully resolved to '/main.ts'. ========",
"======== Resolving type reference directive 'lib', containing file '/__inferred type names__.ts', root directory '/types'. ========",
"Resolving with primary search path '/types'",

View file

@ -16,9 +16,7 @@
"Resolving real path for '/types/lib/index.d.ts', result '/types/lib/index.d.ts'",
"======== Type reference directive 'lib' was successfully resolved to '/types/lib/index.d.ts', primary: true. ========",
"======== Resolving module './main' from '/mod1.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module as file / folder, candidate module location '/main'.",
"File '/main.ts' exist - use it as a name resolution result.",
"Resolution for module './main' was found in cache",
"======== Module name './main' was successfully resolved to '/main.ts'. ========",
"======== Resolving type reference directive 'lib', containing file '/__inferred type names__.ts', root directory '/types'. ========",
"Resolving with primary search path '/types'",

View file

@ -0,0 +1,12 @@
// @module: amd
// @importHelpers: true
// @traceResolution: true
// @filename: /a/b/c/app.ts
export let x = 1;
// @filename: /a/b/c/lib1.ts
export let x = 1;
// @filename: /a/b/c/lib2.ts
export let x = 1;