Skip to content

Commit

Permalink
Merge pull request #31100 from Microsoft/tsBuildCacheModuleResolution
Browse files Browse the repository at this point in the history
Cache module resolutions across tsbuild to be able to resolve the modules faster
  • Loading branch information
sheetalkamat committed Apr 26, 2019
2 parents 0949ad1 + 9bb8373 commit 94ee0dc
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 13 deletions.
32 changes: 24 additions & 8 deletions src/compiler/moduleNameResolver.ts
Expand Up @@ -421,6 +421,7 @@ namespace ts {
*/
export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache {
getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map<ResolvedModuleWithFailedLookupLocations>;
/*@internal*/ directoryToModuleNameMap: CacheWithRedirects<Map<ResolvedModuleWithFailedLookupLocations>>;
}

/**
Expand All @@ -429,49 +430,64 @@ namespace ts {
*/
export interface NonRelativeModuleNameResolutionCache {
getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
/*@internal*/ moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>;
}

export interface PerModuleNameCache {
get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined;
set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
}

export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache {
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string, options?: CompilerOptions): ModuleResolutionCache {
return createModuleResolutionCacheWithMaps(
createCacheWithRedirects(),
createCacheWithRedirects(),
createCacheWithRedirects(options),
createCacheWithRedirects(options),
currentDirectory,
getCanonicalFileName
);
}


/*@internal*/
export interface CacheWithRedirects<T> {
ownMap: Map<T>;
redirectsMap: Map<Map<T>>;
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<T>;
clear(): void;
setOwnOptions(newOptions: CompilerOptions): void;
setOwnMap(newOwnMap: Map<T>): void;
}

/*@internal*/
export function createCacheWithRedirects<T>(): CacheWithRedirects<T> {
const ownMap: Map<T> = createMap();
export function createCacheWithRedirects<T>(options?: CompilerOptions): CacheWithRedirects<T> {
let ownMap: Map<T> = createMap();
const redirectsMap: Map<Map<T>> = createMap();
return {
ownMap,
redirectsMap,
getOrCreateMapOfCacheRedirects,
clear
clear,
setOwnOptions,
setOwnMap
};

function setOwnOptions(newOptions: CompilerOptions) {
options = newOptions;
}

function setOwnMap(newOwnMap: Map<T>) {
ownMap = newOwnMap;
}

function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) {
if (!redirectedReference) {
return ownMap;
}
const path = redirectedReference.sourceFile.path;
let redirects = redirectsMap.get(path);
if (!redirects) {
redirects = createMap();
// Reuse map if redirected reference map uses same resolution
redirects = !options || optionsHaveModuleResolutionChanges(options, redirectedReference.commandLine.options) ? createMap() : ownMap;
redirectsMap.set(path, redirects);
}
return redirects;
Expand All @@ -490,7 +506,7 @@ namespace ts {
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache {

return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName };
return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName, directoryToModuleNameMap, moduleNameToDirectoryMap };

function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/program.ts
Expand Up @@ -528,7 +528,8 @@ namespace ts {
}
}

function loadWithLocalCache<T>(names: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
/* @internal */
export function loadWithLocalCache<T>(names: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
if (names.length === 0) {
return [];
}
Expand Down Expand Up @@ -773,7 +774,7 @@ namespace ts {
});
}
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x), options);
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule!; // TODO: GH#18217
resolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames, redirectedReference) => loadWithLocalCache<ResolvedModuleFull>(Debug.assertEachDefined(moduleNames), containingFile, redirectedReference, loader);
}
Expand Down
37 changes: 37 additions & 0 deletions src/compiler/tsbuild.ts
Expand Up @@ -400,6 +400,10 @@ namespace ts {
const compilerHost = createCompilerHostFromProgramHost(host, () => projectCompilerOptions);
setGetSourceFileAsHashVersioned(compilerHost, host);

compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
let moduleResolutionCache = !compilerHost.resolveModuleNames ? createModuleResolutionCache(currentDirectory, getCanonicalFileName) : undefined;

const buildInfoChecked = createFileMap<true>(toPath);

// Watch state
Expand Down Expand Up @@ -1097,6 +1101,30 @@ namespace ts {

// TODO: handle resolve module name to cache result in project reference redirect
projectCompilerOptions = configFile.options;
// Update module resolution cache if needed
if (moduleResolutionCache) {
const projPath = toPath(proj);
if (moduleResolutionCache.directoryToModuleNameMap.redirectsMap.size === 0) {
// The own map will be for projectCompilerOptions
Debug.assert(moduleResolutionCache.moduleNameToDirectoryMap.redirectsMap.size === 0);
moduleResolutionCache.directoryToModuleNameMap.redirectsMap.set(projPath, moduleResolutionCache.directoryToModuleNameMap.ownMap);
moduleResolutionCache.moduleNameToDirectoryMap.redirectsMap.set(projPath, moduleResolutionCache.moduleNameToDirectoryMap.ownMap);
}
else {
// Set correct own map
Debug.assert(moduleResolutionCache.moduleNameToDirectoryMap.redirectsMap.size > 0);

const ref: ResolvedProjectReference = {
sourceFile: projectCompilerOptions.configFile!,
commandLine: configFile
};
moduleResolutionCache.directoryToModuleNameMap.setOwnMap(moduleResolutionCache.directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref));
moduleResolutionCache.moduleNameToDirectoryMap.setOwnMap(moduleResolutionCache.moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref));
}
moduleResolutionCache.directoryToModuleNameMap.setOwnOptions(projectCompilerOptions);
moduleResolutionCache.moduleNameToDirectoryMap.setOwnOptions(projectCompilerOptions);
}

const program = host.createProgram(
configFile.fileNames,
configFile.options,
Expand Down Expand Up @@ -1368,6 +1396,13 @@ namespace ts {
readFileWithCache = newReadFileWithCache;
compilerHost.getSourceFile = getSourceFileWithCache!;

const originalResolveModuleNames = compilerHost.resolveModuleNames;
if (!compilerHost.resolveModuleNames) {
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference).resolvedModule!;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference) =>
loadWithLocalCache<ResolvedModuleFull>(Debug.assertEachDefined(moduleNames), containingFile, redirectedReference, loader);
}

const graph = getGlobalDependencyGraph();
reportBuildQueue(graph);
let anyFailed = false;
Expand Down Expand Up @@ -1428,6 +1463,8 @@ namespace ts {
host.writeFile = originalWriteFile;
compilerHost.getSourceFile = savedGetSourceFile;
readFileWithCache = savedReadFileWithCache;
compilerHost.resolveModuleNames = originalResolveModuleNames;
moduleResolutionCache = undefined;
return anyFailed ? ExitStatus.DiagnosticsPresent_OutputsSkipped : ExitStatus.Success;
}

Expand Down
7 changes: 6 additions & 1 deletion src/compiler/utilities.ts
Expand Up @@ -101,7 +101,12 @@ namespace ts {
}

export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean {
return oldOptions.configFilePath !== newOptions.configFilePath || moduleResolutionOptionDeclarations.some(o =>
return oldOptions.configFilePath !== newOptions.configFilePath ||
optionsHaveModuleResolutionChanges(oldOptions, newOptions);
}

export function optionsHaveModuleResolutionChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions) {
return moduleResolutionOptionDeclarations.some(o =>
!isJsonEqual(getCompilerOptionValue(oldOptions, o), getCompilerOptionValue(newOptions, o)));
}

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Expand Up @@ -3719,7 +3719,7 @@ declare namespace ts {
get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined;
set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
}
function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache;
function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string, options?: CompilerOptions): ModuleResolutionCache;
function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations | undefined;
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/api/typescript.d.ts
Expand Up @@ -3719,7 +3719,7 @@ declare namespace ts {
get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined;
set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
}
function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache;
function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string, options?: CompilerOptions): ModuleResolutionCache;
function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations | undefined;
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
Expand Down

0 comments on commit 94ee0dc

Please sign in to comment.