Api cleanup for Module and Type Reference directive resolution (#51546)

* Refactoring so CacheWithRedirects has Key and Value type parameters

* ModuleResolutionCache or TypeRefDirectiveCache will look in directory before solving, so ResolutionCache doesnt need this check

* Test showing module resolution is not shared because resolution cache doesnt update own options

* Enable traceResolution on some of the project reference tests

* Simplify CacheWithRedirects and ensure the options are set in all common scenarios so cache can be shared between redirects

* Make failedlookup etc optional in ResolvedModule/TypeRefefWithFailedLookupLocations
Also make accidental public failed lookup internal

* Add new API for module and type ref resolution

* Store auto type reference resolutions

* Modify test to show how using program partially doesnt report resolution diagnostics

* Ensure that resolution diagnostics are reported in filePreocessingDiagnostics so they can be reused when program is reused

* Some cleanup

* Remove the newly added ReoslutionInfo in favor of new APIs

* update
This commit is contained in:
Sheetal Nandi
2022-12-05 11:56:33 -08:00
committed by GitHub
parent c07f51242c
commit 9e845d2248
73 changed files with 2770 additions and 1438 deletions

View File

@@ -233,7 +233,7 @@ export namespace BuilderState {
// Handle type reference directives
if (sourceFile.resolvedTypeReferenceDirectiveNames) {
sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => {
sourceFile.resolvedTypeReferenceDirectiveNames.forEach(({ resolvedTypeReferenceDirective }) => {
if (!resolvedTypeReferenceDirective) {
return;
}

View File

@@ -1333,8 +1333,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
host.getSourceFiles().forEach(sf => {
if (!sf.resolvedModules) return;
sf.resolvedModules.forEach(r => {
if (r && r.packageId) map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
sf.resolvedModules.forEach(({ resolvedModule }) => {
if (resolvedModule?.packageId) map.set(resolvedModule.packageId.name, resolvedModule.extension === Extension.Dts || !!map.get(resolvedModule.packageId.name));
});
});
return map;
@@ -44897,15 +44897,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// this variable and functions that use it are deliberately moved here from the outer scope
// to avoid scope pollution
const resolvedTypeReferenceDirectives = host.getResolvedTypeReferenceDirectives();
let fileToDirective: Map<string, [specifier: string, mode: ResolutionMode | undefined]>;
let fileToDirective: Map<string, [specifier: string, mode: ResolutionMode]>;
if (resolvedTypeReferenceDirectives) {
// populate reverse mapping: file path -> type reference directive that was resolved to this file
fileToDirective = new Map<string, [specifier: string, mode: ResolutionMode | undefined]>();
resolvedTypeReferenceDirectives.forEach((resolvedDirective, key, mode) => {
if (!resolvedDirective || !resolvedDirective.resolvedFileName) {
fileToDirective = new Map<string, [specifier: string, mode: ResolutionMode]>();
resolvedTypeReferenceDirectives.forEach(({ resolvedTypeReferenceDirective }, key, mode) => {
if (!resolvedTypeReferenceDirective?.resolvedFileName) {
return;
}
const file = host.getSourceFile(resolvedDirective.resolvedFileName);
const file = host.getSourceFile(resolvedTypeReferenceDirective.resolvedFileName);
if (file) {
// Add the transitive closure of path references loaded by this file (as long as they are not)
// part of an existing type reference.
@@ -45032,7 +45032,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
// defined here to avoid outer scope pollution
function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode | undefined][] | undefined {
function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode][] | undefined {
// program does not have any files with type reference directives - bail out
if (!fileToDirective) {
return undefined;
@@ -45057,13 +45057,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
// defined here to avoid outer scope pollution
function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: ResolutionMode | undefined][] | undefined {
function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: ResolutionMode][] | undefined {
// program does not have any files with type reference directives - bail out
if (!fileToDirective || !isSymbolFromTypeDeclarationFile(symbol)) {
return undefined;
}
// check what declarations in the symbol can contribute to the target meaning
let typeReferenceDirectives: [specifier: string, mode: ResolutionMode | undefined][] | undefined;
let typeReferenceDirectives: [specifier: string, mode: ResolutionMode][] | undefined;
for (const decl of symbol.declarations!) {
// check meaning of the local symbol to see if declaration needs to be analyzed further
if (decl.symbol && decl.symbol.flags & meaning!) {
@@ -45114,7 +45114,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return false;
}
function addReferencedFilesToTypeDirective(file: SourceFile, key: string, mode: ResolutionMode | undefined) {
function addReferencedFilesToTypeDirective(file: SourceFile, key: string, mode: ResolutionMode) {
if (fileToDirective.has(file.path)) return;
fileToDirective.set(file.path, [key, mode]);
for (const { fileName, resolutionMode } of file.referencedFiles) {

View File

@@ -27,7 +27,6 @@ import {
extensionIsTS,
fileExtensionIs,
fileExtensionIsOneOf,
FileReference,
filter,
firstDefined,
forEach,
@@ -41,7 +40,6 @@ import {
GetEffectiveTypeRootsHost,
getEmitModuleKind,
getEmitModuleResolutionKind,
getModeForUsageLocation,
getNormalizedAbsolutePath,
getOwnKeys,
getPathComponents,
@@ -58,7 +56,6 @@ import {
isExternalModuleNameRelative,
isRootedDiskPath,
isString,
isStringLiteralLike,
lastOrUndefined,
length,
MapLike,
@@ -86,6 +83,7 @@ import {
removeFileExtension,
removePrefix,
ResolutionMode,
ResolutionNameAndModeGetter,
ResolvedModuleWithFailedLookupLocations,
ResolvedProjectReference,
ResolvedTypeReferenceDirective,
@@ -95,10 +93,8 @@ import {
SourceFile,
startsWith,
stringContains,
StringLiteralLike,
supportedDeclarationExtensions,
supportedTSImplementationExtensions,
toFileNameLowerCase,
toPath,
tryExtractTSExtension,
tryGetExtensionFromPath,
@@ -213,17 +209,28 @@ function createResolvedModuleWithFailedLookupLocations(
resultFromCache: ResolvedModuleWithFailedLookupLocations | undefined
): ResolvedModuleWithFailedLookupLocations {
if (resultFromCache) {
resultFromCache.failedLookupLocations.push(...failedLookupLocations);
resultFromCache.affectingLocations.push(...affectingLocations);
resultFromCache.failedLookupLocations = updateResolutionField(resultFromCache.failedLookupLocations, failedLookupLocations);
resultFromCache.affectingLocations = updateResolutionField(resultFromCache.affectingLocations, affectingLocations);
resultFromCache.resolutionDiagnostics = updateResolutionField(resultFromCache.resolutionDiagnostics, diagnostics);
return resultFromCache;
}
return {
resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath: resolved.originalPath === true ? undefined : resolved.originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
failedLookupLocations,
affectingLocations,
resolutionDiagnostics: diagnostics,
failedLookupLocations: initializeResolutionField(failedLookupLocations),
affectingLocations: initializeResolutionField(affectingLocations),
resolutionDiagnostics: initializeResolutionField(diagnostics),
};
}
function initializeResolutionField<T>(value: T[]): T[] | undefined {
return value.length ? value : undefined;
}
/** @internal */
export function updateResolutionField<T>(to: T[] | undefined, value: T[] | undefined) {
if (!value?.length) return to;
if (!to?.length) return value;
to.push(...value);
return to;
}
/** @internal */
export interface ModuleResolutionState {
@@ -524,7 +531,12 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
isExternalLibraryImport: pathContainsNodeModules(fileName),
};
}
result = { resolvedTypeReferenceDirective, failedLookupLocations, affectingLocations, resolutionDiagnostics: diagnostics };
result = {
resolvedTypeReferenceDirective,
failedLookupLocations: initializeResolutionField(failedLookupLocations),
affectingLocations: initializeResolutionField(affectingLocations),
resolutionDiagnostics: initializeResolutionField(diagnostics),
};
perFolderCache?.set(typeReferenceDirectiveName, /*mode*/ resolutionMode, result);
if (traceEnabled) traceResult(result);
return result;
@@ -911,23 +923,17 @@ export function createModeAwareCache<T>(): ModeAwareCache<T> {
}
/** @internal */
export function getResolutionName(entry: string | FileReference | StringLiteralLike) {
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
return !isString(entry) ? isStringLiteralLike(entry) ? entry.text : toFileNameLowerCase(entry.fileName) : entry;
}
/** @internal */
export function getResolutionMode(entry: FileReference | StringLiteralLike, file: SourceFile) {
return isStringLiteralLike(entry) ? getModeForUsageLocation(file, entry) : entry.resolutionMode || file.impliedNodeFormat;
}
/** @internal */
export function zipToModeAwareCache<V>(file: SourceFile, keys: readonly StringLiteralLike[] | readonly FileReference[], values: readonly V[]): ModeAwareCache<V> {
export function zipToModeAwareCache<K, V>(
file: SourceFile,
keys: readonly K[],
values: readonly V[],
nameAndModeGetter: ResolutionNameAndModeGetter<K, SourceFile>,
): ModeAwareCache<V> {
Debug.assert(keys.length === values.length);
const map = createModeAwareCache<V>();
for (let i = 0; i < keys.length; ++i) {
const entry = keys[i];
map.set(getResolutionName(entry), getResolutionMode(entry, file), values[i]);
map.set(nameAndModeGetter.getName(entry), nameAndModeGetter.getMode(entry, file), values[i]);
}
return map;
}
@@ -1373,7 +1379,7 @@ function tryLoadModuleUsingBaseUrl(extensions: Extensions, moduleName: string, l
export function resolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string {
const { resolvedModule, failedLookupLocations } = tryResolveJSModuleWorker(moduleName, initialDir, host);
if (!resolvedModule) {
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`);
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations?.join(", ")}`);
}
return resolvedModule.resolvedFileName;
}

View File

@@ -15,7 +15,6 @@ import {
changeExtension,
changesAffectingProgramStructure,
changesAffectModuleResolution,
clone,
combinePaths,
CommentDirective,
CommentDirectivesMap,
@@ -42,6 +41,7 @@ import {
createGetCanonicalFileName,
createInputFilesWithFilePaths,
createModeAwareCache,
createModeAwareCacheKey,
createModuleResolutionCache,
createMultiMap,
CreateProgramOptions,
@@ -131,10 +131,7 @@ import {
getPositionOfLineAndCharacter,
getPropertyArrayElementValue,
getPropertyAssignment,
getResolutionMode,
getResolutionName,
getResolvedModule,
getResolvedTypeReferenceDirective,
getRootLength,
getSetExternalModuleIndicator,
getSpellingSuggestion,
@@ -207,6 +204,8 @@ import {
maybeBind,
memoize,
MethodDeclaration,
ModeAwareCache,
ModeAwareCacheKey,
ModifierFlags,
ModifierLike,
ModuleBlock,
@@ -214,7 +213,6 @@ import {
ModuleKind,
ModuleResolutionCache,
ModuleResolutionHost,
ModuleResolutionInfo,
moduleResolutionIsEqualTo,
ModuleResolutionKind,
Mutable,
@@ -261,9 +259,8 @@ import {
ResolvedModuleFull,
ResolvedModuleWithFailedLookupLocations,
ResolvedProjectReference,
ResolvedTypeReferenceDirective,
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
resolveModuleName,
resolveModuleNameFromCache,
resolveTypeReferenceDirective,
returnFalse,
returnUndefined,
@@ -304,7 +301,6 @@ import {
TypeChecker,
typeDirectiveIsEqualTo,
TypeReferenceDirectiveResolutionCache,
TypeReferenceDirectiveResolutionInfo,
UnparsedSource,
VariableDeclaration,
VariableStatement,
@@ -808,28 +804,6 @@ export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageCha
return result;
}
/** @internal */
export function loadWithTypeDirectiveCache<T>(names: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, containingFileMode: ResolutionMode, loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined, resolutionMode: ResolutionMode) => T): T[] {
if (names.length === 0) {
return [];
}
const resolutions: T[] = [];
const cache = createModeAwareCache<T>();
for (const name of names) {
let result: T;
const mode = getModeForFileReference(name, containingFileMode);
const strName = getResolutionName(name);
if (cache.has(strName, mode)) {
result = cache.get(strName, mode)!;
}
else {
cache.set(strName, mode, result = loader(strName, containingFile, redirectedReference, mode));
}
resolutions.push(result);
}
return resolutions;
}
/**
* Subset of a SourceFile used to calculate index-based resolutions
* This includes some internal fields, so unless you have very good reason,
@@ -937,26 +911,117 @@ export function getResolutionModeOverrideForClause(clause: AssertClause | undefi
return elem.value.text === "import" ? ModuleKind.ESNext : ModuleKind.CommonJS;
}
const emptyResolution: ResolvedModuleWithFailedLookupLocations & ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
resolvedModule: undefined,
resolvedTypeReferenceDirective: undefined,
};
/** @internal */
export function loadWithModeAwareCache<T>(names: readonly StringLiteralLike[] | readonly string[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, resolutionInfo: ModuleResolutionInfo | undefined, loader: (name: string, resolverMode: ResolutionMode, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
if (names.length === 0) {
return [];
}
const resolutions: T[] = [];
const cache = createModeAwareCache<T>();
let i = 0;
for (const entry of resolutionInfo ? resolutionInfo.names : names) {
let result: T;
const mode = !isString(entry) ?
getModeForUsageLocation(containingFile, entry) :
getModeForResolutionAtIndex(containingFile, i);
i++;
const name = isString(entry) ? entry : entry.text;
if (cache.has(name, mode)) {
result = cache.get(name, mode)!;
}
else {
cache.set(name, mode, result = loader(name, mode, containingFileName, redirectedReference));
export interface ResolutionNameAndModeGetter<Entry, SourceFile> {
getName(entry: Entry): string;
getMode(entry: Entry, file: SourceFile): ResolutionMode;
}
/** @internal */
export interface ResolutionLoader<Entry, Resolution, SourceFile> {
nameAndMode: ResolutionNameAndModeGetter<Entry, SourceFile>;
resolve(name: string, mode: ResolutionMode): Resolution;
}
function getModuleResolutionName(literal: StringLiteralLike) {
return literal.text;
}
/** @internal */
export const moduleResolutionNameAndModeGetter: ResolutionNameAndModeGetter<StringLiteralLike, SourceFile> = {
getName: getModuleResolutionName,
getMode: (entry, file) => getModeForUsageLocation(file, entry),
};
/** @internal */
export function createModuleResolutionLoader(
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
host: ModuleResolutionHost,
cache: ModuleResolutionCache | undefined,
): ResolutionLoader<StringLiteralLike, ResolvedModuleWithFailedLookupLocations, SourceFile> {
return {
nameAndMode: moduleResolutionNameAndModeGetter,
resolve: (moduleName, resoluionMode) => resolveModuleName(
moduleName,
containingFile,
options,
host,
cache,
redirectedReference,
resoluionMode,
),
};
}
function getTypeReferenceResolutionName<T extends FileReference | string>(entry: T) {
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
return !isString(entry) ? toFileNameLowerCase(entry.fileName) : entry;
}
/** @internal */
export const typeReferenceResolutionNameAndModeGetter: ResolutionNameAndModeGetter<FileReference | string, SourceFile | undefined> = {
getName: getTypeReferenceResolutionName,
getMode: (entry, file) => getModeForFileReference(entry, file?.impliedNodeFormat),
};
/** @internal */
export function createTypeReferenceResolutionLoader<T extends FileReference | string>(
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
host: ModuleResolutionHost,
cache: TypeReferenceDirectiveResolutionCache | undefined,
): ResolutionLoader<T, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, SourceFile | undefined> {
return {
nameAndMode: typeReferenceResolutionNameAndModeGetter,
resolve: (typeRef, resoluionMode) => resolveTypeReferenceDirective(
typeRef,
containingFile,
options,
host,
redirectedReference,
cache,
resoluionMode,
),
};
}
/** @internal */
export function loadWithModeAwareCache<Entry, SourceFile, ResolutionCache, Resolution>(
entries: readonly Entry[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile,
host: ModuleResolutionHost,
resolutionCache: ResolutionCache | undefined,
createLoader: (
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
host: ModuleResolutionHost,
resolutionCache: ResolutionCache | undefined,
) => ResolutionLoader<Entry, Resolution, SourceFile>,
): readonly Resolution[] {
if (entries.length === 0) return emptyArray;
const resolutions: Resolution[] = [];
const cache = new Map<ModeAwareCacheKey, Resolution>();
const loader = createLoader(containingFile, redirectedReference, options, host, resolutionCache);
for (const entry of entries) {
const name = loader.nameAndMode.getName(entry);
const mode = loader.nameAndMode.getMode(entry, containingSourceFile);
const key = createModeAwareCacheKey(name, mode);
let result = cache.get(key);
if (!result) {
cache.set(key, result = loader.resolve(name, mode));
}
resolutions.push(result);
}
@@ -1057,7 +1122,7 @@ export function getReferencedFileLocation(getSourceFileByPath: (path: Path) => S
switch (kind) {
case FileIncludeKind.Import:
const importLiteral = getModuleNameStringLiteralAt(file, index);
packageId = file.resolvedModules?.get(importLiteral.text, getModeForResolutionAtIndex(file, index))?.packageId;
packageId = file.resolvedModules?.get(importLiteral.text, getModeForResolutionAtIndex(file, index))?.resolvedModule?.packageId;
if (importLiteral.pos === -1) return { file, packageId, text: importLiteral.text };
pos = skipTrivia(file.text, importLiteral.pos);
end = importLiteral.end;
@@ -1067,7 +1132,7 @@ export function getReferencedFileLocation(getSourceFileByPath: (path: Path) => S
break;
case FileIncludeKind.TypeReferenceDirective:
({ pos, end, resolutionMode } = file.typeReferenceDirectives[index]);
packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName), resolutionMode || file.impliedNodeFormat)?.packageId;
packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName), resolutionMode || file.impliedNodeFormat)?.resolvedTypeReferenceDirective?.packageId;
break;
case FileIncludeKind.LibReferenceDirective:
({ pos, end } = file.libReferenceDirectives[index]);
@@ -1376,8 +1441,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
let resolvedTypeReferenceDirectives = createModeAwareCache<ResolvedTypeReferenceDirective | undefined>();
let resolvedTypeReferenceDirectives = createModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>();
let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined;
let automaticTypeDirectiveNames: string[] | undefined;
let automaticTypeDirectiveResolutions: ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
// This works as imported modules are discovered recursively in a depth first manner, specifically:
@@ -1405,6 +1472,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
let skipDefaultLib = options.noLib;
const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options));
const defaultLibraryPath = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(getDefaultLibraryFileName());
/**
* Diagnostics for the program
* Only add diagnostics directly if it always would be done irrespective of program structure reuse.
* Otherwise fileProcessingDiagnostics is correct locations so that the diagnostics can be reported in all structure use scenarios
*/
const programDiagnostics = createDiagnosticCollection();
const currentDirectory = host.getCurrentDirectory();
const supportedExtensions = getSupportedExtensions(options);
@@ -1415,67 +1487,86 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | false | undefined;
let moduleResolutionCache: ModuleResolutionCache | undefined;
let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
let actualResolveModuleNamesWorker: (
moduleNames: readonly StringLiteralLike[],
containingFile: SourceFile,
containingFileName: string,
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
resolutionInfo: ModuleResolutionInfo | undefined,
) => (ResolvedModuleFull | undefined)[];
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
) => readonly ResolvedModuleWithFailedLookupLocations[];
const hasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse;
if (host.resolveModuleNames) {
actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo) =>
if (host.resolveModuleNameLiterals) {
actualResolveModuleNamesWorker = host.resolveModuleNameLiterals.bind(host);
moduleResolutionCache = host.getModuleResolutionCache?.();
}
else if (host.resolveModuleNames) {
actualResolveModuleNamesWorker = (moduleNames, containingFile, redirectedReference, options, containingSourceFile, reusedNames) =>
host.resolveModuleNames!(
moduleNames.map(literal => literal.text),
containingFileName,
resolutionInfo?.reusedNames?.map(literal => literal.text),
moduleNames.map(getModuleResolutionName),
containingFile,
reusedNames?.map(getModuleResolutionName),
redirectedReference,
options,
containingFile,
resolutionInfo,
).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
return resolved as ResolvedModuleFull;
}
const withExtension = clone(resolved) as ResolvedModuleFull;
withExtension.extension = extensionFromPath(resolved.resolvedFileName);
return withExtension;
});
containingSourceFile,
).map(resolved => resolved ?
((resolved as ResolvedModuleFull).extension !== undefined) ?
{ resolvedModule: resolved as ResolvedModuleFull } :
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
{ resolvedModule: { ...resolved, extension: extensionFromPath(resolved.resolvedFileName) } } :
emptyResolution
);
moduleResolutionCache = host.getModuleResolutionCache?.();
}
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options);
const loader = (moduleName: string, resolverMode: ResolutionMode, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) =>
resolveModuleName(moduleName, containingFileName, options, host, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule;
actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo) =>
loadWithModeAwareCache(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo, loader);
actualResolveModuleNamesWorker = (moduleNames, containingFile, redirectedReference, options, containingSourceFile) =>
loadWithModeAwareCache(
moduleNames,
containingFile,
redirectedReference,
options,
containingSourceFile,
host,
moduleResolutionCache,
createModuleResolutionLoader,
);
}
let actualResolveTypeReferenceDirectiveNamesWorker: (
typeDirectiveNames: string[] | readonly FileReference[],
let actualResolveTypeReferenceDirectiveNamesWorker: <T extends FileReference | string>(
typeDirectiveNames: T[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
containingFileMode: ResolutionMode | undefined,
resolutionInfo: TypeReferenceDirectiveResolutionInfo | undefined,
) => (ResolvedTypeReferenceDirective | undefined)[];
if (host.resolveTypeReferenceDirectives) {
actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, containingFileMode, resolutionInfo) =>
host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options, containingFileMode, resolutionInfo);
options: CompilerOptions,
containingSourceFile: SourceFile | undefined,
reusedNames: readonly T[] | undefined,
) => readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
if (host.resolveTypeReferenceDirectiveReferences) {
actualResolveTypeReferenceDirectiveNamesWorker = host.resolveTypeReferenceDirectiveReferences.bind(host);
}
else if (host.resolveTypeReferenceDirectives) {
actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, options, containingSourceFile) =>
host.resolveTypeReferenceDirectives!(
typeDirectiveNames.map(getTypeReferenceResolutionName),
containingFile,
redirectedReference,
options,
containingSourceFile?.impliedNodeFormat,
).map(resolvedTypeReferenceDirective => ({ resolvedTypeReferenceDirective }));
}
else {
typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache());
const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined, resolutionMode: ResolutionMode | undefined) => resolveTypeReferenceDirective(
typesRef,
containingFile,
options,
host,
redirectedReference,
typeReferenceDirectiveResolutionCache,
resolutionMode,
).resolvedTypeReferenceDirective!; // TODO: GH#18217
actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference, containingFileMode) => loadWithTypeDirectiveCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, containingFileMode, loader);
const typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache());
actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, options, containingSourceFile) =>
loadWithModeAwareCache(
typeDirectiveNames,
containingFile,
redirectedReference,
options,
containingSourceFile,
host,
typeReferenceDirectiveResolutionCache,
createTypeReferenceResolutionLoader,
);
}
// Map from a stringified PackageId to the source file with that id.
@@ -1569,17 +1660,27 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
tracing?.pop();
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray;
if (typeReferences.length) {
tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length });
automaticTypeDirectiveNames ??= rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray;
automaticTypeDirectiveResolutions = createModeAwareCache();
if (automaticTypeDirectiveNames.length) {
tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: automaticTypeDirectiveNames.length });
// This containingFilename needs to match with the one used in managed-side
const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typeReferences, containingFilename);
for (let i = 0; i < typeReferences.length; i++) {
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(automaticTypeDirectiveNames, containingFilename);
for (let i = 0; i < automaticTypeDirectiveNames.length; i++) {
// under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode
processTypeReferenceDirective(typeReferences[i], /*mode*/ undefined, resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId });
automaticTypeDirectiveResolutions.set(automaticTypeDirectiveNames[i], /*mode*/ undefined, resolutions[i]);
processTypeReferenceDirective(
automaticTypeDirectiveNames[i],
/*mode*/ undefined,
resolutions[i],
{
kind: FileIncludeKind.AutomaticTypeDirectiveFile,
typeReference: automaticTypeDirectiveNames[i],
packageId: resolutions[i]?.resolvedTypeReferenceDirective?.packageId,
},
);
}
tracing?.pop();
}
@@ -1646,8 +1747,6 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
);
}
typeReferenceDirectiveResolutionCache = undefined;
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined;
@@ -1682,6 +1781,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
getRelationCacheSizes: () => getTypeChecker().getRelationCacheSizes(),
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
getAutomaticTypeDirectiveNames: () => automaticTypeDirectiveNames!,
getAutomaticTypeDirectiveResolutions: () => automaticTypeDirectiveResolutions,
isSourceFileFromExternalLibrary,
isSourceFileDefaultLibrary,
getSourceFileFromReference,
@@ -1691,7 +1792,6 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
usesUriStyleNodeCoreModules,
isEmittedFile,
getConfigFileParsingDiagnostics,
getResolvedModuleWithFailedLookupLocationsFromCache,
getProjectReferences,
getResolvedProjectReferences,
getProjectReferenceRedirect,
@@ -1721,6 +1821,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
case FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic:
const { file, pos, end } = getReferencedFileLocation(getSourceFileByPath, diagnostic.reason) as ReferenceFileLocation;
return programDiagnostics.add(createFileDiagnostic(file, Debug.checkDefined(pos), Debug.checkDefined(end) - pos, diagnostic.diagnostic, ...diagnostic.args || emptyArray));
case FilePreprocessingDiagnosticsKind.ResolutionDiagnostics:
return diagnostic.diagnostics.forEach(d => programDiagnostics.add(d));
default:
Debug.assertNever(diagnostic);
}
@@ -1733,55 +1835,51 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return program;
function addResolutionDiagnostics(list: Diagnostic[] | undefined) {
if (!list) return;
for (const elem of list) {
programDiagnostics.add(elem);
}
function addResolutionDiagnostics(resolution: ResolvedModuleWithFailedLookupLocations | ResolvedTypeReferenceDirectiveWithFailedLookupLocations) {
if (!resolution.resolutionDiagnostics?.length) return;
(fileProcessingDiagnostics ??= []).push({
kind: FilePreprocessingDiagnosticsKind.ResolutionDiagnostics,
diagnostics: resolution.resolutionDiagnostics
});
}
function pullDiagnosticsFromCache(names: readonly StringLiteralLike[] | readonly FileReference[], containingFile: SourceFile) {
if (!moduleResolutionCache) return;
function addResolutionDiagnosticsFromResolutionOrCache(containingFile: SourceFile, name: string, resolution: ResolvedModuleWithFailedLookupLocations, mode: ResolutionMode) {
// diagnostics directly from the resolution
if (host.resolveModuleNameLiterals || !host.resolveModuleNames) return addResolutionDiagnostics(resolution);
if (!moduleResolutionCache || isExternalModuleNameRelative(name)) return;
const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
const containingDir = getDirectoryPath(containingFileName);
const redirectedReference = getRedirectReferenceForResolution(containingFile);
for (const n of names) {
// mimics logic done in the resolution cache, should be resilient to upgrading it to use `FileReference`s for non-type-reference modal lookups to make it rely on the index in the list less
const mode = getResolutionMode(n, containingFile);
const name = getResolutionName(n);
// only nonrelative names hit the cache, and, at least as of right now, only nonrelative names can issue diagnostics
// (Since diagnostics are only issued via import or export map lookup)
// This may totally change if/when the issue of output paths not mapping to input files is fixed in a broader context
// When it is, how we extract diagnostics from the module name resolver will have the be refined - the current cache
// APIs wrapping the underlying resolver make it almost impossible to smuggle the diagnostics out in a generalized way
if (isExternalModuleNameRelative(name)) continue;
const diags = moduleResolutionCache.getOrCreateCacheForModuleName(name, mode, redirectedReference).get(containingDir)?.resolutionDiagnostics;
addResolutionDiagnostics(diags);
}
// only nonrelative names hit the cache, and, at least as of right now, only nonrelative names can issue diagnostics
// (Since diagnostics are only issued via import or export map lookup)
// This may totally change if/when the issue of output paths not mapping to input files is fixed in a broader context
// When it is, how we extract diagnostics from the module name resolver will have the be refined - the current cache
// APIs wrapping the underlying resolver make it almost impossible to smuggle the diagnostics out in a generalized way
const fromCache = moduleResolutionCache.getOrCreateCacheForModuleName(name, mode, redirectedReference).get(containingDir);
if (fromCache) addResolutionDiagnostics(fromCache);
}
function resolveModuleNamesWorker(moduleNames: readonly StringLiteralLike[], containingFile: SourceFile, resolutionInfo: ModuleResolutionInfo | undefined): readonly (ResolvedModuleFull | undefined)[] {
function resolveModuleNamesWorker(moduleNames: readonly StringLiteralLike[], containingFile: SourceFile, reusedNames: readonly StringLiteralLike[] | undefined): readonly ResolvedModuleWithFailedLookupLocations[] {
if (!moduleNames.length) return emptyArray;
const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
const redirectedReference = getRedirectReferenceForResolution(containingFile);
tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName });
performance.mark("beforeResolveModule");
const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo);
const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, redirectedReference, options, containingFile, reusedNames);
performance.mark("afterResolveModule");
performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
tracing?.pop();
pullDiagnosticsFromCache(moduleNames, containingFile);
return result;
}
function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string | SourceFile, resolutionInfo: TypeReferenceDirectiveResolutionInfo | undefined): readonly (ResolvedTypeReferenceDirective | undefined)[] {
function resolveTypeReferenceDirectiveNamesWorker<T extends FileReference | string>(typeDirectiveNames: T[], containingFile: string | SourceFile, reusedNames: readonly T[] | undefined): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] {
if (!typeDirectiveNames.length) return [];
const containingSourceFile = !isString(containingFile) ? containingFile : undefined;
const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile;
const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined;
const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined;
const redirectedReference = containingSourceFile && getRedirectReferenceForResolution(containingSourceFile);
tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName });
performance.mark("beforeResolveTypeReference");
const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference, containingFileMode, resolutionInfo);
const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference, options, containingSourceFile, reusedNames);
performance.mark("afterResolveTypeReference");
performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference");
tracing?.pop();
@@ -1833,10 +1931,6 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return libs.length + 2;
}
function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, mode?: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined {
return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache, mode);
}
function toPath(fileName: string): Path {
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
}
@@ -1869,11 +1963,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return classifiableNames;
}
function resolveModuleNamesReusingOldState(moduleNames: readonly StringLiteralLike[], file: SourceFile): readonly (ResolvedModuleFull | undefined)[] {
function resolveModuleNamesReusingOldState(moduleNames: readonly StringLiteralLike[], file: SourceFile): readonly ResolvedModuleWithFailedLookupLocations[] {
if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, file, /*resolutionInfo*/ undefined);
return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
}
const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
@@ -1886,9 +1980,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
// which per above occurred during the current program creation.
// Since we assume the filesystem does not change during program creation,
// it is safe to reuse resolutions from the earlier call.
const result: (ResolvedModuleFull | undefined)[] = [];
const result: ResolvedModuleWithFailedLookupLocations[] = [];
for (const moduleName of moduleNames) {
const resolvedModule = file.resolvedModules.get(moduleName.text, getModeForUsageLocation(file, moduleName));
const resolvedModule = file.resolvedModules.get(moduleName.text, getModeForUsageLocation(file, moduleName))!;
result.push(resolvedModule);
}
return result;
@@ -1909,30 +2003,30 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
* Needs to be reset to undefined before returning,
* * ResolvedModuleFull instance: can be reused.
*/
let result: (ResolvedModuleFull | undefined)[] | undefined;
let result: ResolvedModuleWithFailedLookupLocations[] | undefined;
let reusedNames: StringLiteralLike[] | undefined;
/** A transient placeholder used to mark predicted resolution in the result list. */
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = {} as any;
const predictedToResolveToAmbientModuleMarker: ResolvedModuleWithFailedLookupLocations = emptyResolution;
for (let i = 0; i < moduleNames.length; i++) {
const moduleName = moduleNames[i];
// If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
if (file === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path)) {
const mode = getModeForUsageLocation(file, moduleName);
const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName.text, mode);
if (oldResolvedModule) {
const oldResolution = oldSourceFile.resolvedModules?.get(moduleName.text, mode);
if (oldResolution?.resolvedModule) {
if (isTraceEnabled(options, host)) {
trace(host,
oldResolvedModule.packageId ?
oldResolution.resolvedModule.packageId ?
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2,
moduleName.text,
getNormalizedAbsolutePath(file.originalFileName, currentDirectory),
oldResolvedModule.resolvedFileName,
oldResolvedModule.packageId && packageIdToString(oldResolvedModule.packageId)
oldResolution.resolvedModule.resolvedFileName,
oldResolution.resolvedModule.packageId && packageIdToString(oldResolution.resolvedModule.packageId)
);
}
(result ??= new Array(moduleNames.length))[i] = oldResolvedModule;
(result ??= new Array(moduleNames.length))[i] = oldResolution;
(reusedNames ??= []).push(moduleName);
continue;
}
@@ -1962,7 +2056,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
const resolutions = unknownModuleNames && unknownModuleNames.length
? resolveModuleNamesWorker(unknownModuleNames, file, { names: unknownModuleNames, reusedNames })
? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames)
: emptyArray;
// Combine results of resolutions and predicted results
@@ -1974,14 +2068,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
let j = 0;
for (let i = 0; i < result.length; i++) {
if (result[i]) {
// `result[i]` is either a `ResolvedModuleFull` or a marker.
// If it is the former, we can leave it as is.
if (result[i] === predictedToResolveToAmbientModuleMarker) {
result[i] = undefined;
}
}
else {
if (!result[i]) {
result[i] = resolutions[j];
j++;
}
@@ -2017,13 +2104,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
}
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: readonly FileReference[], containingFile: SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[];
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: string[], containingFile: string): readonly (ResolvedTypeReferenceDirective | undefined)[];
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] {
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: readonly FileReference[], containingFile: SourceFile): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: string[], containingFile: string): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
function resolveTypeReferenceDirectiveNamesReusingOldState<T extends string | FileReference>(typeDirectiveNames: T[], containingFile: string | SourceFile): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] {
if (structureIsReused === StructureIsReused.Not) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFile, /*resolutionInfo*/ undefined);
return resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFile, /*resuedNames*/ undefined);
}
const oldSourceFile = !isString(containingFile) ? oldProgram && oldProgram.getSourceFile(containingFile.fileName) : undefined;
@@ -2037,10 +2124,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
// which per above occurred during the current program creation.
// Since we assume the filesystem does not change during program creation,
// it is safe to reuse resolutions from the earlier call.
const result: (ResolvedTypeReferenceDirective | undefined)[] = [];
const result: ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] = [];
for (const typeDirectiveName of typeDirectiveNames as readonly FileReference[]) {
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
const resolvedTypeReferenceDirective = containingFile.resolvedTypeReferenceDirectiveNames.get(getResolutionName(typeDirectiveName), typeDirectiveName.resolutionMode || containingFile.impliedNodeFormat);
const resolvedTypeReferenceDirective = containingFile.resolvedTypeReferenceDirectiveNames.get(getTypeReferenceResolutionName(typeDirectiveName), getModeForFileReference(typeDirectiveName, containingFile.impliedNodeFormat))!;
result.push(resolvedTypeReferenceDirective);
}
return result;
@@ -2048,9 +2135,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
/** An ordered list of module names for which we cannot recover the resolution. */
let unknownTypeReferenceDirectiveNames: string[] | FileReference[] | undefined;
let result: (ResolvedTypeReferenceDirective | undefined)[] | undefined;
let reusedNames: (string | FileReference)[] | undefined;
let unknownTypeReferenceDirectiveNames: T[] | undefined;
let result: ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] | undefined;
let reusedNames: T[] | undefined;
const containingSourceFile = !isString(containingFile) ? containingFile : undefined;
const canReuseResolutions = !isString(containingFile) ?
containingFile === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path) :
@@ -2058,35 +2145,35 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
for (let i = 0; i < typeDirectiveNames.length; i++) {
const entry = typeDirectiveNames[i];
if (canReuseResolutions) {
const typeDirectiveName = getResolutionName(entry);
const typeDirectiveName = getTypeReferenceResolutionName(entry);
const mode = getModeForFileReference(entry, containingSourceFile?.impliedNodeFormat);
const oldResolvedTypeReferenceDirective = getResolvedTypeReferenceDirective(oldSourceFile, typeDirectiveName, mode);
if (oldResolvedTypeReferenceDirective) {
const oldResolution = (!isString(containingFile) ? oldSourceFile?.resolvedTypeReferenceDirectiveNames : oldProgram?.getAutomaticTypeDirectiveResolutions())?.get(typeDirectiveName, mode);
if (oldResolution?.resolvedTypeReferenceDirective) {
if (isTraceEnabled(options, host)) {
trace(host,
oldResolvedTypeReferenceDirective.packageId ?
oldResolution.resolvedTypeReferenceDirective.packageId ?
Diagnostics.Reusing_resolution_of_type_reference_directive_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
Diagnostics.Reusing_resolution_of_type_reference_directive_0_from_1_of_old_program_it_was_successfully_resolved_to_2,
typeDirectiveName,
!isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile,
oldResolvedTypeReferenceDirective.resolvedFileName,
oldResolvedTypeReferenceDirective.packageId && packageIdToString(oldResolvedTypeReferenceDirective.packageId)
oldResolution.resolvedTypeReferenceDirective.resolvedFileName,
oldResolution.resolvedTypeReferenceDirective.packageId && packageIdToString(oldResolution.resolvedTypeReferenceDirective.packageId)
);
}
(result ??= new Array(typeDirectiveNames.length))[i] = oldResolvedTypeReferenceDirective;
(result ??= new Array(typeDirectiveNames.length))[i] = oldResolution;
(reusedNames ??= []).push(entry);
continue;
}
}
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
(unknownTypeReferenceDirectiveNames ??= []).push(entry as FileReference & string);
(unknownTypeReferenceDirectiveNames ??= []).push(entry);
}
if (!unknownTypeReferenceDirectiveNames) return result || emptyArray;
const resolutions = resolveTypeReferenceDirectiveNamesWorker(
unknownTypeReferenceDirectiveNames,
containingFile,
{ names: unknownTypeReferenceDirectiveNames, reusedNames }
reusedNames,
);
// Combine results of resolutions
@@ -2301,10 +2388,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
const moduleNames = getModuleNames(newSourceFile);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile);
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, newSourceFile, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
const resolutionsChanged = hasChangesInResolutions(moduleNames, newSourceFile, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo, moduleResolutionNameAndModeGetter);
if (resolutionsChanged) {
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedModules = zipToModeAwareCache(newSourceFile, moduleNames, resolutions);
newSourceFile.resolvedModules = zipToModeAwareCache(newSourceFile, moduleNames, resolutions, moduleResolutionNameAndModeGetter);
}
else {
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
@@ -2312,10 +2399,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
const typesReferenceDirectives = newSourceFile.typeReferenceDirectives;
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typesReferenceDirectives, newSourceFile);
// ensure that types resolutions are still correct
const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, newSourceFile, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, newSourceFile, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo, typeReferenceResolutionNameAndModeGetter);
if (typeReferenceResolutionsChanged) {
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToModeAwareCache(newSourceFile, typesReferenceDirectives, typeReferenceResolutions);
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToModeAwareCache(newSourceFile, typesReferenceDirectives, typeReferenceResolutions, typeReferenceResolutionNameAndModeGetter);
}
else {
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
@@ -2326,10 +2413,17 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return structureIsReused;
}
if (changesAffectingProgramStructure(oldOptions, options) || host.hasChangedAutomaticTypeDirectiveNames?.()) {
if (changesAffectingProgramStructure(oldOptions, options)) {
return StructureIsReused.SafeModules;
}
if (host.hasChangedAutomaticTypeDirectiveNames) {
if (host.hasChangedAutomaticTypeDirectiveNames()) return StructureIsReused.SafeModules;
}
else {
automaticTypeDirectiveNames = getAutomaticTypeDirectiveNames(options, host);
if (!arrayIsEqualTo(oldProgram.getAutomaticTypeDirectiveNames(), automaticTypeDirectiveNames)) return StructureIsReused.SafeModules;
}
missingFilePaths = oldProgram.getMissingFilePaths();
// update fileName -> file mapping
@@ -2357,6 +2451,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
fileReasons = oldProgram.getFileIncludeReasons();
fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
automaticTypeDirectiveNames = oldProgram.getAutomaticTypeDirectiveNames();
automaticTypeDirectiveResolutions = oldProgram.getAutomaticTypeDirectiveResolutions();
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
redirectTargetsMap = oldProgram.redirectTargetsMap;
@@ -3573,7 +3669,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
function processTypeReferenceDirectives(file: SourceFile) {
const typeDirectives = file.typeReferenceDirectives;
if (!typeDirectives) {
if (!typeDirectives.length) {
file.resolvedTypeReferenceDirectiveNames = undefined;
return;
}
@@ -3586,7 +3683,12 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective, getModeForFileReference(ref, file.impliedNodeFormat));
const mode = ref.resolutionMode || file.impliedNodeFormat;
if (mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) {
programDiagnostics.add(createDiagnosticForRange(file, ref, Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext));
(fileProcessingDiagnostics ??= []).push({
kind: FilePreprocessingDiagnosticsKind.ResolutionDiagnostics,
diagnostics: [
createDiagnosticForRange(file, ref, Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext)
]
});
}
processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
}
@@ -3594,28 +3696,29 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
function processTypeReferenceDirective(
typeReferenceDirective: string,
mode: ResolutionMode | undefined,
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
mode: ResolutionMode,
resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
reason: FileIncludeReason
): void {
tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolvedTypeReferenceDirective, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolvedTypeReferenceDirective, reason);
tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolution.resolvedTypeReferenceDirective, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolution, reason);
tracing?.pop();
}
function processTypeReferenceDirectiveWorker(
typeReferenceDirective: string,
mode: ResolutionMode | undefined,
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
mode: ResolutionMode,
resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
reason: FileIncludeReason
): void {
addResolutionDiagnostics(resolution);
// If we already found this library as a primary reference - nothing to do
const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode);
const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode)?.resolvedTypeReferenceDirective;
if (previousResolution && previousResolution.primary) {
return;
}
let saveResolution = true;
const { resolvedTypeReferenceDirective } = resolution;
if (resolvedTypeReferenceDirective) {
if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++;
@@ -3656,7 +3759,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
if (saveResolution) {
resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolvedTypeReferenceDirective);
resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolution);
}
}
@@ -3714,8 +3817,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
Debug.assert(resolutions.length === moduleNames.length);
const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options;
for (let index = 0; index < moduleNames.length; index++) {
const resolution = resolutions[index];
setResolvedModule(file, moduleNames[index].text, resolution, getModeForUsageLocation(file, moduleNames[index]));
const resolution = resolutions[index].resolvedModule;
const moduleName = moduleNames[index].text;
const mode = getModeForUsageLocation(file, moduleNames[index]);
setResolvedModule(file, moduleName, resolutions[index], mode);
addResolutionDiagnosticsFromResolutionOrCache(file, moduleName, resolutions[index], mode);
if (!resolution) {
continue;
@@ -4479,8 +4585,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
if (!symlinks) {
symlinks = createSymlinkCache(currentDirectory, getCanonicalFileName);
}
if (files && resolvedTypeReferenceDirectives && !symlinks.hasProcessedResolutions()) {
symlinks.setSymlinksFromResolutions(files, resolvedTypeReferenceDirectives);
if (files && automaticTypeDirectiveResolutions && !symlinks.hasProcessedResolutions()) {
symlinks.setSymlinksFromResolutions(files, automaticTypeDirectiveResolutions);
}
return symlinks;
}

View File

@@ -7,11 +7,11 @@ import {
closeFileWatcher,
closeFileWatcherOf,
CompilerOptions,
contains,
createModeAwareCache,
createModuleResolutionCache,
createMultiMap,
createTypeReferenceDirectiveResolutionCache,
createTypeReferenceResolutionLoader,
Debug,
Diagnostics,
directorySeparator,
@@ -30,11 +30,7 @@ import {
GetCanonicalFileName,
getDirectoryPath,
getEffectiveTypeRoots,
getModeForFileReference,
getModeForResolutionAtIndex,
getModeForUsageLocation,
getNormalizedAbsolutePath,
getResolutionName,
getRootLength,
HasInvalidatedResolutions,
ignoredPaths,
@@ -44,8 +40,6 @@ import {
isExternalOrCommonJsModule,
isNodeModulesDirectory,
isRootedDiskPath,
isString,
isStringLiteralLike,
isTraceEnabled,
length,
loadModuleFromGlobalCache,
@@ -53,8 +47,7 @@ import {
MinimalResolutionCacheHost,
ModeAwareCache,
ModuleResolutionCache,
ModuleResolutionHost,
ModuleResolutionInfo,
moduleResolutionNameAndModeGetter,
mutateMap,
noopFileWatcher,
normalizePath,
@@ -67,20 +60,20 @@ import {
removeSuffix,
removeTrailingDirectorySeparator,
resolutionExtensionIsTSOrJson,
ResolutionLoader,
ResolutionMode,
ResolvedModuleFull,
ResolvedModuleWithFailedLookupLocations,
ResolvedProjectReference,
ResolvedTypeReferenceDirective,
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
returnTrue,
some,
SourceFile,
startsWith,
stringContains,
StringLiteralLike,
trace,
TypeReferenceDirectiveResolutionInfo,
unorderedRemoveItem,
updateResolutionField,
WatchDirectoryFlags,
} from "./_namespaces/ts";
@@ -93,22 +86,27 @@ export interface ResolutionCache {
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
resolveModuleNames(
moduleNames: string[],
resolveModuleNameLiterals(
moduleLiterals: readonly StringLiteralLike[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
): readonly ResolvedModuleWithFailedLookupLocations[];
resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile | undefined,
resolutionInfo: ModuleResolutionInfo | undefined
): (ResolvedModuleFull | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ResolutionMode): CachedResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives(
typeDirectiveNames: string[] | readonly FileReference[],
reusedNames: readonly T[] | undefined
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
resolveSingleModuleNameWithoutWatching(
moduleName: string,
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
containingFileMode: ResolutionMode,
resolutionInfo: TypeReferenceDirectiveResolutionInfo | undefined,
): (ResolvedTypeReferenceDirective | undefined)[];
): ResolvedModuleWithFailedLookupLocations;
invalidateResolutionsOfFailedLookupLocations(): boolean;
invalidateResolutionOfFile(filePath: Path): void;
@@ -133,8 +131,8 @@ export interface ResolutionCache {
/** @internal */
export interface ResolutionWithFailedLookupLocations {
readonly failedLookupLocations: string[];
readonly affectingLocations: string[];
failedLookupLocations?: string[];
affectingLocations?: string[];
isInvalidated?: boolean;
refCount?: number;
// Files that have this resolution using
@@ -329,9 +327,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
startCachingPerDirectoryResolution,
finishCachingPerDirectoryResolution,
resolveModuleNames,
getResolvedModuleWithFailedLookupLocationsFromCache,
resolveTypeReferenceDirectives,
resolveModuleNameLiterals,
resolveTypeReferenceDirectiveReferences,
resolveSingleModuleNameWithoutWatching,
removeResolutionsFromProjectReferenceRedirects,
removeResolutionsOfFile,
hasChangedAutomaticTypeDirectiveNames: () => hasChangedAutomaticTypeDirectiveNames,
@@ -466,7 +464,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
hasChangedAutomaticTypeDirectiveNames = false;
}
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, _containingSourceFile?: never, mode?: ResolutionMode): CachedResolvedModuleWithFailedLookupLocations {
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, redirectedReference?: ResolvedProjectReference, mode?: ResolutionMode): CachedResolvedModuleWithFailedLookupLocations {
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference, mode);
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
if (!resolutionHost.getGlobalCache) {
@@ -478,7 +477,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTS(primaryResult.resolvedModule.extension))) {
// create different collection of failed lookup locations for second pass
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
const { resolvedModule, failedLookupLocations, affectingLocations } = loadModuleFromGlobalCache(
const { resolvedModule, failedLookupLocations, affectingLocations, resolutionDiagnostics } = loadModuleFromGlobalCache(
Debug.checkDefined(resolutionHost.globalCacheResolutionModuleName)(moduleName),
resolutionHost.projectName,
compilerOptions,
@@ -489,8 +488,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
if (resolvedModule) {
// Modify existing resolution so its saved in the directory cache as well
(primaryResult.resolvedModule as any) = resolvedModule;
primaryResult.failedLookupLocations.push(...failedLookupLocations);
primaryResult.affectingLocations.push(...affectingLocations);
primaryResult.failedLookupLocations = updateResolutionField(primaryResult.failedLookupLocations, failedLookupLocations);
primaryResult.affectingLocations = updateResolutionField(primaryResult.affectingLocations, affectingLocations);
primaryResult.resolutionDiagnostics = updateResolutionField(primaryResult.resolutionDiagnostics, resolutionDiagnostics);
return primaryResult;
}
}
@@ -499,34 +499,45 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
return primaryResult;
}
function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, _containingSourceFile?: SourceFile, resolutionMode?: ResolutionMode): CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations {
return ts.resolveTypeReferenceDirective(typeReferenceDirectiveName, containingFile, options, host, redirectedReference, typeReferenceDirectiveResolutionCache, resolutionMode);
function createModuleResolutionLoader(
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
): ResolutionLoader<StringLiteralLike, ResolvedModuleWithFailedLookupLocations, SourceFile> {
return {
nameAndMode: moduleResolutionNameAndModeGetter,
resolve: (moduleName, resoluionMode) => resolveModuleName(
moduleName,
containingFile,
options,
redirectedReference,
resoluionMode,
),
};
}
interface ResolveNamesWithLocalCacheInput<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName> {
names: readonly string[] | readonly FileReference[];
interface ResolveNamesWithLocalCacheInput<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName> {
entries: readonly Entry[];
containingFile: string;
containingSourceFile: SourceFile;
redirectedReference: ResolvedProjectReference | undefined;
options: CompilerOptions;
reusedNames?: readonly Entry[];
perFileCache: Map<Path, ModeAwareCache<T>>;
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile, resolutionMode?: ResolutionMode) => T;
loader: ResolutionLoader<Entry, T, SourceFile>;
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
shouldRetryResolution: (t: T) => boolean;
reusedNames?: readonly string[];
resolutionInfo?: ModuleResolutionInfo | TypeReferenceDirectiveResolutionInfo;
logChanges?: boolean;
containingSourceFile?: SourceFile;
containingSourceFileMode?: ResolutionMode;
}
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
names, containingFile, redirectedReference,
perFileCache,
function resolveNamesWithLocalCache<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
entries, containingFile, containingSourceFile, redirectedReference, options,
perFileCache, reusedNames,
loader, getResolutionWithResolvedFileName,
shouldRetryResolution, reusedNames, resolutionInfo, logChanges, containingSourceFile, containingSourceFileMode
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
shouldRetryResolution, logChanges,
}: ResolveNamesWithLocalCacheInput<Entry, SourceFile, T, R>): readonly T[] {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = perFileCache.get(path) || perFileCache.set(path, createModeAwareCache()).get(path)!;
const resolvedModules: (R | undefined)[] = [];
const compilerOptions = resolutionHost.getCompilationSettings();
const resolvedModules: T[] = [];
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
// All the resolutions in this file are invalidated if this file wasn't resolved using same redirect
@@ -537,22 +548,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
!!redirectedReference;
const seenNamesInFile = createModeAwareCache<true>();
let i = 0;
for (const entry of containingSourceFile && resolutionInfo ? resolutionInfo.names : names) {
const name = getResolutionName(entry);
// Imports supply a `containingSourceFile` but no `containingSourceFileMode` - it would be redundant
// they require calculating the mode for a given import from it's position in the resolution table, since a given
// import's syntax may override the file's default mode.
// Type references instead supply a `containingSourceFileMode` and a non-string entry which contains
// a default file mode override if applicable.
const mode = !isString(entry) ?
isStringLiteralLike(entry) ?
getModeForUsageLocation(containingSourceFile!, entry) :
getModeForFileReference(entry, containingSourceFileMode) :
containingSourceFile ?
getModeForResolutionAtIndex(containingSourceFile, i) :
undefined;
i++;
for (const entry of entries) {
const name = loader.nameAndMode.getName(entry);
const mode = loader.nameAndMode.getMode(entry, containingSourceFile);
let resolution = resolutionsInFile.get(name, mode);
// Resolution is valid if it is present and not invalidated
if (!seenNamesInFile.has(name, mode) &&
@@ -560,7 +558,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
// If the name is unresolved import that was invalidated, recalculate
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
const existingResolution = resolution;
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference, containingSourceFile, mode);
resolution = loader.resolve(name, mode);
if (resolutionHost.onDiscoveredSymlink && resolutionIsSymlink(resolution)) {
resolutionHost.onDiscoveredSymlink();
}
@@ -578,11 +576,11 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
else {
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
if (isTraceEnabled(compilerOptions, host) && !seenNamesInFile.has(name, mode)) {
if (isTraceEnabled(options, host) && !seenNamesInFile.has(name, mode)) {
const resolved = getResolutionWithResolvedFileName(resolution);
trace(
host,
loader === resolveModuleName as unknown ?
perFileCache === resolvedModuleNames as unknown ?
resolved?.resolvedFileName ?
resolved.packageId ?
Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
@@ -602,30 +600,22 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
Debug.assert(resolution !== undefined && !resolution.isInvalidated);
seenNamesInFile.set(name, mode, true);
resolvedModules.push(getResolutionWithResolvedFileName(resolution));
}
// If resolving type reference directive we dont need containingSourceFile to determine if we can use resolutionInfo
if (resolutionInfo && (loader === resolveTypeReferenceDirective as unknown || containingSourceFile)) {
resolutionInfo.reusedNames?.forEach(entry => seenNamesInFile.set(
getResolutionName(entry),
!isString(entry) && isStringLiteralLike(entry) ?
getModeForUsageLocation(containingSourceFile!, entry) :
getModeForFileReference(entry, containingSourceFileMode),
true,
));
reusedNames = undefined;
resolvedModules.push(resolution);
}
reusedNames?.forEach(entry => seenNamesInFile.set(
loader.nameAndMode.getName(entry),
loader.nameAndMode.getMode(entry, containingSourceFile),
true,
));
if (resolutionsInFile.size() !== seenNamesInFile.size()) {
// Stop watching and remove the unused name
resolutionsInFile.forEach((resolution, name, mode) => {
if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) {
if (!seenNamesInFile.has(name, mode)) {
stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName);
resolutionsInFile.delete(name, mode);
}
});
}
return resolvedModules;
function resolutionIsEqualTo(oldResolution: T | undefined, newResolution: T | undefined): boolean {
@@ -647,53 +637,67 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
}
function resolveTypeReferenceDirectives(
typeDirectiveNames: string[] | readonly FileReference[],
function resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
containingFile: string,
redirectedReference?: ResolvedProjectReference,
containingFileMode?: ResolutionMode,
resolutionInfo?: TypeReferenceDirectiveResolutionInfo,
): (ResolvedTypeReferenceDirective | undefined)[] {
return resolveNamesWithLocalCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations, ResolvedTypeReferenceDirective>({
names: typeDirectiveNames,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile | undefined,
reusedNames: readonly T[] | undefined
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[]{
return resolveNamesWithLocalCache({
entries: typeDirectiveReferences,
containingFile,
containingSourceFile,
redirectedReference,
options,
reusedNames,
perFileCache: resolvedTypeReferenceDirectives,
loader: resolveTypeReferenceDirective,
loader: createTypeReferenceResolutionLoader(
containingFile,
redirectedReference,
options,
resolutionHost.getCompilerHost?.() || resolutionHost,
typeReferenceDirectiveResolutionCache
),
getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective,
shouldRetryResolution: resolution => resolution.resolvedTypeReferenceDirective === undefined,
containingSourceFileMode: containingFileMode,
resolutionInfo,
});
}
function resolveModuleNames(
moduleNames: string[],
function resolveModuleNameLiterals(
moduleLiterals: readonly StringLiteralLike[],
containingFile: string,
reusedNames: string[] | undefined,
redirectedReference?: ResolvedProjectReference,
containingSourceFile?: SourceFile,
resolutionInfo?: ModuleResolutionInfo
): (ResolvedModuleFull | undefined)[] {
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>({
names: moduleNames,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
): readonly ResolvedModuleWithFailedLookupLocations[] {
return resolveNamesWithLocalCache({
entries: moduleLiterals,
containingFile,
containingSourceFile,
redirectedReference,
options,
reusedNames,
perFileCache: resolvedModuleNames,
loader: resolveModuleName,
loader: createModuleResolutionLoader(
containingFile,
redirectedReference,
options,
),
getResolutionWithResolvedFileName: getResolvedModule,
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
reusedNames,
resolutionInfo,
logChanges: logChangesWhenResolvingModule,
containingSourceFile,
});
}
function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ResolutionMode): CachedResolvedModuleWithFailedLookupLocations | undefined {
const cache = resolvedModuleNames.get(resolutionHost.toPath(containingFile));
if (!cache) return undefined;
return cache.get(moduleName, resolutionMode);
function resolveSingleModuleNameWithoutWatching(moduleName: string, containingFile: string) {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = resolvedModuleNames.get(path);
const resolution = resolutionsInFile?.get(moduleName, /*mode*/ undefined);
if (resolution && !resolution.isInvalidated) return resolution;
return resolveModuleName(moduleName, containingFile, resolutionHost.getCompilationSettings());
}
function isNodeModulesAtTypesDirectory(dirPath: Path) {
@@ -797,42 +801,44 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
Debug.assert(!!resolution.refCount);
const { failedLookupLocations, affectingLocations } = resolution;
if (!failedLookupLocations.length && !affectingLocations.length) return;
if (failedLookupLocations.length) resolutionsWithFailedLookups.push(resolution);
if (!failedLookupLocations?.length && !affectingLocations?.length) return;
if (failedLookupLocations?.length) resolutionsWithFailedLookups.push(resolution);
let setAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const toWatch = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (toWatch) {
const { dir, dirPath, nonRecursive } = toWatch;
// If the failed lookup location path is not one of the supported extensions,
// store it in the custom path
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
}
if (dirPath === rootPath) {
Debug.assert(!nonRecursive);
setAtRoot = true;
}
else {
setDirectoryWatcher(dir, dirPath, nonRecursive);
if (failedLookupLocations) {
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const toWatch = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (toWatch) {
const { dir, dirPath, nonRecursive } = toWatch;
// If the failed lookup location path is not one of the supported extensions,
// store it in the custom path
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
}
if (dirPath === rootPath) {
Debug.assert(!nonRecursive);
setAtRoot = true;
}
else {
setDirectoryWatcher(dir, dirPath, nonRecursive);
}
}
}
}
if (setAtRoot) {
// This is always non recursive
setDirectoryWatcher(rootDir!, rootPath, /*nonRecursive*/ true); // TODO: GH#18217
if (setAtRoot) {
// This is always non recursive
setDirectoryWatcher(rootDir!, rootPath, /*nonRecursive*/ true); // TODO: GH#18217
}
}
watchAffectingLocationsOfResolution(resolution, !failedLookupLocations.length);
watchAffectingLocationsOfResolution(resolution, !failedLookupLocations?.length);
}
function watchAffectingLocationsOfResolution(resolution: ResolutionWithFailedLookupLocations, addToResolutionsWithOnlyAffectingLocations: boolean) {
Debug.assert(!!resolution.refCount);
const { affectingLocations } = resolution;
if (!affectingLocations.length) return;
if (!affectingLocations?.length) return;
if (addToResolutionsWithOnlyAffectingLocations) resolutionsWithOnlyAffectingLocations.push(resolution);
// Watch package json
for (const affectingLocation of affectingLocations) {
@@ -932,7 +938,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
const { failedLookupLocations, affectingLocations } = resolution;
if (unorderedRemoveItem(resolutionsWithFailedLookups, resolution)) {
let removeAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
for (const failedLookupLocation of failedLookupLocations!) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const toWatch = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (toWatch) {
@@ -960,13 +966,15 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
removeDirectoryWatcher(rootPath);
}
}
else if (affectingLocations.length) {
else if (affectingLocations?.length) {
unorderedRemoveItem(resolutionsWithOnlyAffectingLocations, resolution);
}
for (const affectingLocation of affectingLocations) {
const watcher = fileWatchesOfAffectingLocations.get(affectingLocation)!;
watcher.resolutions--;
if (affectingLocations) {
for (const affectingLocation of affectingLocations) {
const watcher = fileWatchesOfAffectingLocations.get(affectingLocation)!;
watcher.resolutions--;
}
}
}
@@ -1020,7 +1028,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, getResolvedTypeReferenceDirective);
}
function invalidateResolutions(resolutions: ResolutionWithFailedLookupLocations[] | undefined, canInvalidate: (resolution: ResolutionWithFailedLookupLocations) => boolean) {
function invalidateResolutions(resolutions: ResolutionWithFailedLookupLocations[] | undefined, canInvalidate: (resolution: ResolutionWithFailedLookupLocations) => boolean | undefined) {
if (!resolutions) return false;
let invalidated = false;
for (const resolution of resolutions) {
@@ -1130,7 +1138,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
function canInvalidateFailedLookupResolution(resolution: ResolutionWithFailedLookupLocations) {
if (canInvalidatedFailedLookupResolutionWithAffectingLocation(resolution)) return true;
if (!failedLookupChecks && !startsWithPathChecks && !isInDirectoryChecks) return false;
return resolution.failedLookupLocations.some(location => isInvalidatedFailedLookup(resolutionHost.toPath(location)));
return resolution.failedLookupLocations?.some(location => isInvalidatedFailedLookup(resolutionHost.toPath(location)));
}
function isInvalidatedFailedLookup(locationPath: Path) {
@@ -1140,7 +1148,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
function canInvalidatedFailedLookupResolutionWithAffectingLocation(resolution: ResolutionWithFailedLookupLocations) {
return !!affectingPathChecks && resolution.affectingLocations.some(location => affectingPathChecks!.has(location));
return !!affectingPathChecks && resolution.affectingLocations?.some(location => affectingPathChecks!.has(location));
}
function closeTypeRootsWatch() {

View File

@@ -24,9 +24,11 @@ import {
createDiagnosticReporter,
createGetCanonicalFileName,
createModuleResolutionCache,
createModuleResolutionLoader,
CreateProgram,
createProgramHost,
createTypeReferenceDirectiveResolutionCache,
createTypeReferenceResolutionLoader,
createWatchFactory,
createWatchHost,
CustomTransformers,
@@ -75,7 +77,6 @@ import {
isString,
listFiles,
loadWithModeAwareCache,
loadWithTypeDirectiveCache,
map,
maybeBind,
missingFileModifiedTime,
@@ -97,15 +98,10 @@ import {
ProgramMultiFileEmitBuildInfo,
readBuilderProgram,
ReadBuildProgramHost,
ResolutionMode,
resolveConfigFileProjectName,
ResolvedConfigFileName,
ResolvedProjectReference,
ResolvedTypeReferenceDirective,
resolveModuleName,
resolvePath,
resolveProjectReferencePath,
resolveTypeReferenceDirective,
returnUndefined,
SemanticDiagnosticsBuilderProgram,
setGetSourceFileAsHashVersioned,
@@ -438,22 +434,40 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
const compilerHost = createCompilerHostFromProgramHost(host, () => state.projectCompilerOptions) as CompilerHost & ReadBuildProgramHost;
setGetSourceFileAsHashVersioned(compilerHost);
compilerHost.getParsedCommandLine = fileName => parseConfigFile(state, fileName as ResolvedConfigFileName, toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName));
compilerHost.resolveModuleNameLiterals = maybeBind(host, host.resolveModuleNameLiterals);
compilerHost.resolveTypeReferenceDirectiveReferences = maybeBind(host, host.resolveTypeReferenceDirectiveReferences);
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
compilerHost.getModuleResolutionCache = maybeBind(host, host.getModuleResolutionCache);
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, resolverMode: ResolutionMode, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) =>
resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile, resolutionInfo) =>
loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, resolutionInfo, loader);
let moduleResolutionCache: ModuleResolutionCache | undefined, typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
if (!compilerHost.resolveModuleNameLiterals && !compilerHost.resolveModuleNames) {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName);
compilerHost.resolveModuleNameLiterals = (moduleNames, containingFile, redirectedReference, options, containingSourceFile) =>
loadWithModeAwareCache(
moduleNames,
containingFile,
redirectedReference,
options,
containingSourceFile,
host,
moduleResolutionCache,
createModuleResolutionLoader,
);
compilerHost.getModuleResolutionCache = () => moduleResolutionCache;
}
if (!compilerHost.resolveTypeReferenceDirectives) {
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined, containingFileMode: ResolutionMode) => resolveTypeReferenceDirective(moduleName, containingFile, state.projectCompilerOptions, compilerHost, redirectedReference, state.typeReferenceDirectiveResolutionCache, containingFileMode).resolvedTypeReferenceDirective!;
compilerHost.resolveTypeReferenceDirectives = (typeReferenceDirectiveNames, containingFile, redirectedReference, _options, containingFileMode) =>
loadWithTypeDirectiveCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, containingFileMode, loader);
if (!compilerHost.resolveTypeReferenceDirectiveReferences && !compilerHost.resolveTypeReferenceDirectives) {
typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache());
compilerHost.resolveTypeReferenceDirectiveReferences = (typeDirectiveNames, containingFile, redirectedReference, options, containingSourceFile) =>
loadWithModeAwareCache(
typeDirectiveNames,
containingFile,
redirectedReference,
options,
containingSourceFile,
host,
typeReferenceDirectiveResolutionCache,
createTypeReferenceResolutionLoader,
);
}
compilerHost.getBuildInfo = (fileName, configFilePath) => getBuildInfo(state, fileName, toResolvedConfigFilePath(state, configFilePath as ResolvedConfigFileName), /*modifiedTime*/ undefined);

View File

@@ -4114,8 +4114,8 @@ export interface SourceFile extends Declaration {
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
// It is used to resolve module names in the checker.
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
/** @internal */ resolvedModules?: ModeAwareCache<ResolvedModuleFull | undefined>;
/** @internal */ resolvedTypeReferenceDirectiveNames: ModeAwareCache<ResolvedTypeReferenceDirective | undefined>;
/** @internal */ resolvedModules?: ModeAwareCache<ResolvedModuleWithFailedLookupLocations>;
/** @internal */ resolvedTypeReferenceDirectiveNames?: ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
/** @internal */ imports: readonly StringLiteralLike[];
// Identifier only if `declare global`
/** @internal */ moduleAugmentations: readonly (StringLiteral | Identifier)[];
@@ -4386,7 +4386,8 @@ export type FileIncludeReason =
/** @internal */
export const enum FilePreprocessingDiagnosticsKind {
FilePreprocessingReferencedDiagnostic,
FilePreprocessingFileExplainingDiagnostic
FilePreprocessingFileExplainingDiagnostic,
ResolutionDiagnostics,
}
/** @internal */
@@ -4407,7 +4408,13 @@ export interface FilePreprocessingFileExplainingDiagnostic {
}
/** @internal */
export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic;
export interface ResolutionDiagnostics {
kind: FilePreprocessingDiagnosticsKind.ResolutionDiagnostics;
diagnostics: readonly Diagnostic[];
}
/** @internal */
export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic | ResolutionDiagnostics;
/** @internal */
export const enum EmitOnly{
@@ -4483,7 +4490,9 @@ export interface Program extends ScriptReferenceHost {
getRelationCacheSizes(): { assignable: number, identity: number, subtype: number, strictSubtype: number };
/** @internal */ getFileProcessingDiagnostics(): FilePreprocessingDiagnostics[] | undefined;
/** @internal */ getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirective | undefined>;
/** @internal */ getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
/** @internal */ getAutomaticTypeDirectiveNames(): string[];
/** @internal */ getAutomaticTypeDirectiveResolutions(): ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
isSourceFileDefaultLibrary(file: SourceFile): boolean;
@@ -4521,8 +4530,6 @@ export interface Program extends ScriptReferenceHost {
/** @internal */ getFileIncludeReasons(): MultiMap<Path, FileIncludeReason>;
/** @internal */ useCaseSensitiveFileNames(): boolean;
/** @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, mode?: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined;
getProjectReferences(): readonly ProjectReference[] | undefined;
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
/** @internal */ getProjectReferenceRedirect(fileName: string): string | undefined;
@@ -4646,7 +4653,7 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost {
getSourceFiles(): readonly SourceFile[];
getSourceFile(fileName: string): SourceFile | undefined;
getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirective | undefined>;
getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
getProjectReferenceRedirect(fileName: string): string | undefined;
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
@@ -5343,8 +5350,8 @@ export interface EmitResolver {
moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean;
isArgumentsLocalBinding(node: Identifier): boolean;
getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode | ImportCall): SourceFile | undefined;
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode | undefined][] | undefined;
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: ResolutionMode | undefined][] | undefined;
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode][] | undefined;
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: ResolutionMode][] | undefined;
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
getJsxFactoryEntity(location?: Node): EntityName | undefined;
getJsxFragmentFactoryEntity(location?: Node): EntityName | undefined;
@@ -7275,11 +7282,11 @@ export const enum Extension {
export interface ResolvedModuleWithFailedLookupLocations {
readonly resolvedModule: ResolvedModuleFull | undefined;
/** @internal */
readonly failedLookupLocations: string[];
failedLookupLocations?: string[];
/** @internal */
readonly affectingLocations: string[];
affectingLocations?: string[];
/** @internal */
readonly resolutionDiagnostics: Diagnostic[]
resolutionDiagnostics?: Diagnostic[]
}
export interface ResolvedTypeReferenceDirective {
@@ -7300,9 +7307,9 @@ export interface ResolvedTypeReferenceDirective {
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
readonly failedLookupLocations: string[];
/** @internal */ readonly affectingLocations: string[];
/** @internal */ resolutionDiagnostics: Diagnostic[];
/** @internal */ failedLookupLocations?: string[];
/** @internal */ affectingLocations?: string[];
/** @internal */ resolutionDiagnostics?: Diagnostic[];
}
/** @internal */
@@ -7310,13 +7317,6 @@ export type HasInvalidatedResolutions = (sourceFile: Path) => boolean;
/** @internal */
export type HasChangedAutomaticTypeDirectiveNames = () => boolean;
export interface ResolutionInfo<T> {
names: readonly T[];
reusedNames: readonly T[] | undefined;
}
export type ModuleResolutionInfo = ResolutionInfo<StringLiteralLike>;
export type TypeReferenceDirectiveResolutionInfo = ResolutionInfo<string | FileReference>;
export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
@@ -7337,15 +7337,34 @@ export interface CompilerHost extends ModuleResolutionHost {
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
* 'throw new Error("NotImplemented")'
*/
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/** @deprecated supply resolveModuleNameLiterals instead for resolution that can handle newer resolution modes like nodenext */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
/**
* Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it
*/
getModuleResolutionCache?(): ModuleResolutionCache | undefined;
/**
* @deprecated supply resolveTypeReferenceDirectiveReferences instead for resolution that can handle newer resolution modes like nodenext
*
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: ResolutionMode | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[];
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: ResolutionMode): (ResolvedTypeReferenceDirective | undefined)[];
resolveModuleNameLiterals?(
moduleLiterals: readonly StringLiteralLike[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
): readonly ResolvedModuleWithFailedLookupLocations[];
resolveTypeReferenceDirectiveReferences?<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile | undefined,
reusedNames: readonly T[] | undefined
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
getEnvironmentVariable?(name: string): string | undefined;
/** @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void;
/** @internal */ onReleaseParsedCommandLine?(configFileName: string, oldResolvedRef: ResolvedProjectReference | undefined, optionOptions: CompilerOptions): void;

View File

@@ -124,7 +124,6 @@ import {
FileExtensionInfo,
fileExtensionIs,
fileExtensionIsOneOf,
FileReference,
FileWatcher,
filter,
find,
@@ -184,8 +183,6 @@ import {
getPathComponents,
getPathFromPathComponents,
getRelativePathToDirectoryOrUrl,
getResolutionMode,
getResolutionName,
getRootLength,
getSnippetElement,
getStringComparer,
@@ -425,8 +422,11 @@ import {
RequireOrImportCall,
RequireVariableStatement,
ResolutionMode,
ResolutionNameAndModeGetter,
ResolvedModuleFull,
ResolvedModuleWithFailedLookupLocations,
ResolvedTypeReferenceDirective,
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
ReturnStatement,
SatisfiesExpression,
ScriptKind,
@@ -705,11 +705,11 @@ export function getFullWidth(node: Node) {
/** @internal */
export function getResolvedModule(sourceFile: SourceFile | undefined, moduleNameText: string, mode: ResolutionMode): ResolvedModuleFull | undefined {
return sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText, mode);
return sourceFile?.resolvedModules?.get(moduleNameText, mode)?.resolvedModule;
}
/** @internal */
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull | undefined, mode: ResolutionMode): void {
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleWithFailedLookupLocations, mode: ResolutionMode): void {
if (!sourceFile.resolvedModules) {
sourceFile.resolvedModules = createModeAwareCache();
}
@@ -718,7 +718,7 @@ export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string
}
/** @internal */
export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, mode: ResolutionMode): void {
export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, mode: ResolutionMode): void {
if (!sourceFile.resolvedTypeReferenceDirectiveNames) {
sourceFile.resolvedTypeReferenceDirectiveNames = createModeAwareCache();
}
@@ -728,7 +728,7 @@ export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeRe
/** @internal */
export function getResolvedTypeReferenceDirective(sourceFile: SourceFile | undefined, typeReferenceDirectiveName: string, mode: ResolutionMode): ResolvedTypeReferenceDirective | undefined {
return sourceFile?.resolvedTypeReferenceDirectiveNames?.get(typeReferenceDirectiveName, mode);
return sourceFile?.resolvedTypeReferenceDirectiveNames?.get(typeReferenceDirectiveName, mode)?.resolvedTypeReferenceDirective;
}
/** @internal */
@@ -739,12 +739,16 @@ export function projectReferenceIsEqualTo(oldRef: ProjectReference, newRef: Proj
}
/** @internal */
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean {
return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport &&
oldResolution.extension === newResolution.extension &&
oldResolution.resolvedFileName === newResolution.resolvedFileName &&
oldResolution.originalPath === newResolution.originalPath &&
packageIdIsEqual(oldResolution.packageId, newResolution.packageId);
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleWithFailedLookupLocations, newResolution: ResolvedModuleWithFailedLookupLocations): boolean {
return oldResolution === newResolution ||
oldResolution.resolvedModule === newResolution.resolvedModule ||
!!oldResolution.resolvedModule &&
!!newResolution.resolvedModule &&
oldResolution.resolvedModule.isExternalLibraryImport === newResolution.resolvedModule.isExternalLibraryImport &&
oldResolution.resolvedModule.extension === newResolution.resolvedModule.extension &&
oldResolution.resolvedModule.resolvedFileName === newResolution.resolvedModule.resolvedFileName &&
oldResolution.resolvedModule.originalPath === newResolution.resolvedModule.originalPath &&
packageIdIsEqual(oldResolution.resolvedModule.packageId, newResolution.resolvedModule.packageId);
}
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
@@ -762,26 +766,32 @@ export function packageIdToString(packageId: PackageId): string {
}
/** @internal */
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
return oldResolution.resolvedFileName === newResolution.resolvedFileName
&& oldResolution.primary === newResolution.primary
&& oldResolution.originalPath === newResolution.originalPath;
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, newResolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations): boolean {
return oldResolution === newResolution ||
oldResolution.resolvedTypeReferenceDirective === newResolution.resolvedTypeReferenceDirective ||
!!oldResolution.resolvedTypeReferenceDirective &&
!!newResolution.resolvedTypeReferenceDirective &&
oldResolution.resolvedTypeReferenceDirective.resolvedFileName === newResolution.resolvedTypeReferenceDirective.resolvedFileName &&
!!oldResolution.resolvedTypeReferenceDirective.primary === !!newResolution.resolvedTypeReferenceDirective.primary &&
oldResolution.resolvedTypeReferenceDirective.originalPath === newResolution.resolvedTypeReferenceDirective.originalPath;
}
/** @internal */
export function hasChangesInResolutions<T>(
names: readonly StringLiteralLike[] | readonly FileReference[],
export function hasChangesInResolutions<K, V>(
names: readonly K[],
newSourceFile: SourceFile,
newResolutions: readonly T[],
oldResolutions: ModeAwareCache<T> | undefined,
comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
newResolutions: readonly V[],
oldResolutions: ModeAwareCache<V> | undefined,
comparer: (oldResolution: V, newResolution: V) => boolean,
nameAndModeGetter: ResolutionNameAndModeGetter<K, SourceFile>,
): boolean {
Debug.assert(names.length === newResolutions.length);
for (let i = 0; i < names.length; i++) {
const newResolution = newResolutions[i];
const entry = names[i];
const name = getResolutionName(entry);
const mode = getResolutionMode(entry, newSourceFile);
const name = nameAndModeGetter.getName(entry);
const mode = nameAndModeGetter.getMode(entry, newSourceFile);
const oldResolution = oldResolutions && oldResolutions.get(name, mode);
const changed =
oldResolution
@@ -7726,7 +7736,7 @@ export interface SymlinkCache {
* don't include automatic type reference directives. Must be called only when
* `hasProcessedResolutions` returns false (once per cache instance).
*/
setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ModeAwareCache<ResolvedTypeReferenceDirective | undefined> | undefined): void;
setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>): void;
/**
* @internal
* Whether `setSymlinksFromResolutions` has already been called.
@@ -7762,9 +7772,10 @@ export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonic
Debug.assert(!hasProcessedResolutions);
hasProcessedResolutions = true;
for (const file of files) {
file.resolvedModules?.forEach(resolution => processResolution(this, resolution));
file.resolvedModules?.forEach(resolution => processResolution(this, resolution.resolvedModule));
file.resolvedTypeReferenceDirectiveNames?.forEach(resolution => processResolution(this, resolution.resolvedTypeReferenceDirective));
}
typeReferenceDirectives?.forEach(resolution => processResolution(this, resolution));
typeReferenceDirectives.forEach(resolution => processResolution(this, resolution.resolvedTypeReferenceDirective));
},
hasProcessedResolutions: () => hasProcessedResolutions,
};

View File

@@ -57,7 +57,6 @@ import {
MapLike,
maybeBind,
ModuleResolutionCache,
ModuleResolutionInfo,
noop,
noopFileWatcher,
parseConfigHostFromCompilerHostLike,
@@ -69,18 +68,20 @@ import {
ResolutionCacheHost,
ResolutionMode,
ResolvedModule,
ResolvedModuleWithFailedLookupLocations,
ResolvedProjectReference,
ResolvedTypeReferenceDirective,
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
returnFalse,
returnTrue,
ScriptTarget,
setGetSourceFileAsHashVersioned,
SharedExtendedConfigFileWatcher,
SourceFile,
StringLiteralLike,
sys,
System,
toPath,
TypeReferenceDirectiveResolutionInfo,
updateErrorForNoInputFiles,
updateMissingFilePathsWatch,
updateSharedExtendedConfigFileWatcher,
@@ -202,10 +203,34 @@ export interface ProgramHost<T extends BuilderProgram> {
/** If provided is used to get the environment variable */
getEnvironmentVariable?(name: string): string | undefined;
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: ResolutionMode, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[];
/**
* @deprecated supply resolveModuleNameLiterals instead for resolution that can handle newer resolution modes like nodenext
*
* If provided, used to resolve the module names, otherwise typescript's default module resolution
*/
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
/**
* @deprecated supply resolveTypeReferenceDirectiveReferences instead for resolution that can handle newer resolution modes like nodenext
*
* If provided, used to resolve type reference directives, otherwise typescript's default resolution
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: ResolutionMode): (ResolvedTypeReferenceDirective | undefined)[];
resolveModuleNameLiterals?(
moduleLiterals: readonly StringLiteralLike[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
): readonly ResolvedModuleWithFailedLookupLocations[];
resolveTypeReferenceDirectiveReferences?<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
options: CompilerOptions,
containingSourceFile: SourceFile | undefined,
reusedNames: readonly T[] | undefined
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
/** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */
hasInvalidatedResolutions?(filePath: Path): boolean;
/**
@@ -463,16 +488,21 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
/*logChangesWhenResolvingModule*/ false
);
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
compilerHost.resolveModuleNames = host.resolveModuleNames ?
((...args) => host.resolveModuleNames!(...args)) :
((moduleNames, containingFile, reusedNames, redirectedReference, _options, sourceFile, resolutionInfo) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, sourceFile, resolutionInfo));
compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ?
((...args) => host.resolveTypeReferenceDirectives!(...args)) :
((typeDirectiveNames, containingFile, redirectedReference, _options, containingFileMode, resolutionInfo) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode, resolutionInfo));
compilerHost.getModuleResolutionCache = host.resolveModuleNames ?
compilerHost.resolveModuleNameLiterals = maybeBind(host, host.resolveModuleNameLiterals);
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
if (!compilerHost.resolveModuleNameLiterals && !compilerHost.resolveModuleNames) {
compilerHost.resolveModuleNameLiterals = resolutionCache.resolveModuleNameLiterals.bind(resolutionCache);
}
compilerHost.resolveTypeReferenceDirectiveReferences = maybeBind(host, host.resolveTypeReferenceDirectiveReferences);
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
if (!compilerHost.resolveTypeReferenceDirectiveReferences && !compilerHost.resolveTypeReferenceDirectives) {
compilerHost.resolveTypeReferenceDirectiveReferences = resolutionCache.resolveTypeReferenceDirectiveReferences.bind(resolutionCache);
}
compilerHost.getModuleResolutionCache = host.resolveModuleNameLiterals || host.resolveModuleNames ?
maybeBind(host, host.getModuleResolutionCache) :
(() => resolutionCache.getModuleResolutionCache());
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
const userProvidedResolution = !!host.resolveModuleNameLiterals || !!host.resolveTypeReferenceDirectiveReferences ||
!!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
// All resolutions are invalid if user provided resolutions and didnt supply hasInvalidatedResolutions
const customHasInvalidatedResolutions = userProvidedResolution ?
maybeBind(host, host.hasInvalidatedResolutions) || returnTrue :