Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module Resolution and Type Reference directive cache updates and its API changes #43700

Merged
merged 7 commits into from Apr 22, 2021
16 changes: 16 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -4849,6 +4849,22 @@
"category": "Error",
"code": 6238
},
"File '{0}' exists according to earlier cached lookups.": {
"category": "Message",
"code": 6239
},
"File '{0}' does not exist according to earlier cached lookups.": {
"category": "Message",
"code": 6240
},
"Resolution for type reference directive '{0}' was found in cache from location '{1}'.": {
"category": "Message",
"code": 6241
},
"======== Resolving type reference directive '{0}', containing file '{1}'. ========": {
"category": "Message",
"code": 6242
},

"Projects to reference": {
"category": "Message",
Expand Down
287 changes: 230 additions & 57 deletions src/compiler/moduleNameResolver.ts

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions src/compiler/program.ts
Expand Up @@ -843,6 +843,7 @@ namespace ts {
let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | false | undefined;

let moduleResolutionCache: ModuleResolutionCache | undefined;
let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
if (host.resolveModuleNames) {
Expand All @@ -857,7 +858,7 @@ namespace ts {
});
}
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x), options);
moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options);
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule!; // TODO: GH#18217
actualResolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames, redirectedReference) => loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
}
Expand All @@ -867,7 +868,15 @@ namespace ts {
actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options);
}
else {
const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(typesRef, containingFile, options, host, redirectedReference).resolvedTypeReferenceDirective!; // TODO: GH#18217
typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache());
const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(
typesRef,
containingFile,
options,
host,
redirectedReference,
typeReferenceDirectiveResolutionCache,
).resolvedTypeReferenceDirective!; // TODO: GH#18217
actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference) => loadWithLocalCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, loader);
}

Expand Down Expand Up @@ -1036,6 +1045,8 @@ namespace ts {
);
}

typeReferenceDirectiveResolutionCache = undefined;

// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined;

Expand Down
27 changes: 20 additions & 7 deletions src/compiler/resolutionCache.ts
Expand Up @@ -166,15 +166,23 @@ namespace ts {
const resolvedModuleNames = new Map<Path, ESMap<string, CachedResolvedModuleWithFailedLookupLocations>>();
const perDirectoryResolvedModuleNames: CacheWithRedirects<ESMap<string, CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
const moduleResolutionCache = createModuleResolutionCacheWithMaps(
const moduleResolutionCache = createModuleResolutionCache(
getCurrentDirectory(),
resolutionHost.getCanonicalFileName,
/*options*/ undefined,
perDirectoryResolvedModuleNames,
nonRelativeModuleNameCache,
getCurrentDirectory(),
resolutionHost.getCanonicalFileName
);

const resolvedTypeReferenceDirectives = new Map<Path, ESMap<string, CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<ESMap<string, CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
const typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(
getCurrentDirectory(),
resolutionHost.getCanonicalFileName,
/*options*/ undefined,
moduleResolutionCache.getPackageJsonInfoCache(),
perDirectoryResolvedTypeReferenceDirectives
);

/**
* These are the extensions that failed lookup files will have by default,
Expand Down Expand Up @@ -284,9 +292,8 @@ namespace ts {
}

function clearPerDirectoryResolutions() {
perDirectoryResolvedModuleNames.clear();
nonRelativeModuleNameCache.clear();
perDirectoryResolvedTypeReferenceDirectives.clear();
moduleResolutionCache.clear();
typeReferenceDirectiveResolutionCache.clear();
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
nonRelativeExternalModuleResolutions.clear();
}
Expand Down Expand Up @@ -320,7 +327,9 @@ namespace ts {
resolutionHost.projectName,
compilerOptions,
host,
globalCache);
globalCache,
moduleResolutionCache,
);
if (resolvedModule) {
// Modify existing resolution so its saved in the directory cache as well
(primaryResult.resolvedModule as any) = resolvedModule;
Expand All @@ -333,6 +342,10 @@ namespace ts {
return primaryResult;
}

function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations {
return ts.resolveTypeReferenceDirective(typeReferenceDirectiveName, containingFile, options, host, redirectedReference, typeReferenceDirectiveResolutionCache);
}

interface ResolveNamesWithLocalCacheInput<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName> {
names: readonly string[];
containingFile: string;
Expand Down
49 changes: 12 additions & 37 deletions src/compiler/tsbuildPublic.ts
Expand Up @@ -238,6 +238,7 @@ namespace ts {

readonly compilerHost: CompilerHost;
readonly moduleResolutionCache: ModuleResolutionCache | undefined;
readonly typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;

// Mutable state
buildOrder: AnyBuildOrder | undefined;
Expand Down Expand Up @@ -275,11 +276,17 @@ namespace ts {
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
const moduleResolutionCache = !compilerHost.resolveModuleNames ? createModuleResolutionCache(currentDirectory, getCanonicalFileName) : undefined;
const typeReferenceDirectiveResolutionCache = !compilerHost.resolveTypeReferenceDirectives ? createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache()) : undefined;
if (!compilerHost.resolveModuleNames) {
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference).resolvedModule!;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference) =>
loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
}
if (!compilerHost.resolveTypeReferenceDirectives) {
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(moduleName, containingFile, state.projectCompilerOptions, compilerHost, redirectedReference, state.typeReferenceDirectiveResolutionCache).resolvedTypeReferenceDirective!;
compilerHost.resolveTypeReferenceDirectives = (typeReferenceDirectiveNames, containingFile, redirectedReference) =>
loadWithLocalCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, loader);
}

const { watchFile, watchDirectory, writeLog } = createWatchFactory<ResolvedConfigFileName>(hostWithWatch, options);

Expand Down Expand Up @@ -310,6 +317,7 @@ namespace ts {

compilerHost,
moduleResolutionCache,
typeReferenceDirectiveResolutionCache,

// Mutable state
buildOrder: undefined,
Expand Down Expand Up @@ -548,7 +556,7 @@ namespace ts {
function disableCache(state: SolutionBuilderState) {
if (!state.cache) return;

const { cache, host, compilerHost, extendedConfigCache, moduleResolutionCache } = state;
const { cache, host, compilerHost, extendedConfigCache, moduleResolutionCache, typeReferenceDirectiveResolutionCache } = state;

host.readFile = cache.originalReadFile;
host.fileExists = cache.originalFileExists;
Expand All @@ -558,10 +566,8 @@ namespace ts {
compilerHost.getSourceFile = cache.originalGetSourceFile;
state.readFileWithCache = cache.originalReadFileWithCache;
extendedConfigCache.clear();
if (moduleResolutionCache) {
moduleResolutionCache.directoryToModuleNameMap.clear();
moduleResolutionCache.moduleNameToDirectoryMap.clear();
}
moduleResolutionCache?.clear();
typeReferenceDirectiveResolutionCache?.clear();
state.cache = undefined;
}

Expand Down Expand Up @@ -842,7 +848,7 @@ namespace ts {
const { host, compilerHost } = state;
state.projectCompilerOptions = config.options;
// Update module resolution cache if needed
updateModuleResolutionCache(state, project, config);
state.moduleResolutionCache?.update(config.options);

// Create program
program = host.createProgram(
Expand Down Expand Up @@ -1306,37 +1312,6 @@ namespace ts {
return { buildResult, step: BuildStep.QueueReferencingProjects };
}

function updateModuleResolutionCache(
state: SolutionBuilderState,
proj: ResolvedConfigFileName,
config: ParsedCommandLine
) {
if (!state.moduleResolutionCache) return;

// Update module resolution cache if needed
const { moduleResolutionCache } = state;
const projPath = toPath(state, 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: config.options.configFile!,
commandLine: config
};
moduleResolutionCache.directoryToModuleNameMap.setOwnMap(moduleResolutionCache.directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref));
moduleResolutionCache.moduleNameToDirectoryMap.setOwnMap(moduleResolutionCache.moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref));
}
moduleResolutionCache.directoryToModuleNameMap.setOwnOptions(config.options);
moduleResolutionCache.moduleNameToDirectoryMap.setOwnOptions(config.options);
}

function checkConfigFileUpToDateStatus(state: SolutionBuilderState, configFile: string, oldestOutputFileTime: Date, oldestOutputFileName: string): Status.OutOfDateWithSelf | undefined {
// Check tsconfig time
const tsconfigTime = getModifiedTime(state.host, configFile);
Expand Down
2 changes: 1 addition & 1 deletion src/testRunner/unittests/reuseProgramStructure.ts
Expand Up @@ -474,7 +474,7 @@ namespace ts {
"File 'node_modules/@types/a.d.ts' does not exist.",
"File 'node_modules/@types/a/index.d.ts' does not exist.",
"Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/package.json' does not exist according to earlier cached lookups.",
"File 'node_modules/a.js' does not exist.",
"File 'node_modules/a.jsx' does not exist.",
"File 'node_modules/a/index.js' does not exist.",
Expand Down