diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 4f8a5637fd679..12039c33247cd 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -15,7 +15,7 @@ import { patternText, perfLogger, Push, readJson, removeExtension, removeFileExtension, removePrefix, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, some, sort, SourceFile, startsWith, stringContains, - StringLiteralLike, supportedTSExtensionsFlat, toPath, tryExtractTSExtension, tryGetExtensionFromPath, + StringLiteralLike, supportedTSExtensionsFlat, toFileNameLowerCase, toPath, tryExtractTSExtension, tryGetExtensionFromPath, tryParsePatterns, tryRemoveExtension, version, Version, versionMajorMinor, VersionRange, } from "./_namespaces/ts"; @@ -795,9 +795,9 @@ export function createModeAwareCache(): ModeAwareCache { } /** @internal */ -export function getResolutionName(entry: FileReference | StringLiteralLike) { +export function getResolutionName(entry: string | FileReference | StringLiteralLike) { // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. - return isStringLiteralLike(entry) ? entry.text : entry.fileName.toLowerCase(); + return !isString(entry) ? isStringLiteralLike(entry) ? entry.text : toFileNameLowerCase(entry.fileName) : entry; } /** @internal */ diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7a6d8d8b09a3c..0ac682ac4b77f 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -58,7 +58,7 @@ import { targetOptionDeclaration, toFileNameLowerCase, tokenToString, trace, tracing, trimStringEnd, TsConfigSourceFile, TypeChecker, typeDirectiveIsEqualTo, TypeReferenceDirectiveResolutionCache, UnparsedSource, VariableDeclaration, VariableStatement, walkUpParenthesizedExpressions, WriteFileCallback, WriteFileCallbackData, - writeFileEnsuringDirectories, zipToModeAwareCache, + writeFileEnsuringDirectories, zipToModeAwareCache, TypeReferenceDirectiveResolutionInfo, getResolvedTypeReferenceDirective, } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; @@ -564,8 +564,7 @@ export function loadWithTypeDirectiveCache(names: string[] | readonly FileRef for (const name of names) { let result: T; const mode = getModeForFileReference(name, containingFileMode); - // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. - const strName = isString(name) ? name : name.fileName.toLowerCase(); + const strName = getResolutionName(name); const cacheKey = mode !== undefined ? `${mode}|${strName}` : strName; if (cache.has(cacheKey)) { result = cache.get(cacheKey)!; @@ -1202,9 +1201,16 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg loadWithModeAwareCache(moduleNames, containingFile, containingFileName, redirectedReference, resolutionInfo, loader); } - let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined) => (ResolvedTypeReferenceDirective | undefined)[]; + let actualResolveTypeReferenceDirectiveNamesWorker: ( + typeDirectiveNames: string[] | readonly FileReference[], + containingFile: string, + redirectedReference: ResolvedProjectReference | undefined, + containingFileMode: SourceFile["impliedNodeFormat"] | undefined, + resolutionInfo: TypeReferenceDirectiveResolutionInfo | undefined, + ) => (ResolvedTypeReferenceDirective | undefined)[]; if (host.resolveTypeReferenceDirectives) { - actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, containingFileMode) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options, containingFileMode); + actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, containingFileMode, resolutionInfo) => + host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options, containingFileMode, resolutionInfo); } else { typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache()); @@ -1318,7 +1324,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // 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 = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename); + const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typeReferences, containingFilename); for (let i = 0; i < typeReferences.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 }); @@ -1516,14 +1522,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return result; } - function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] { + function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string | SourceFile, resolutionInfo: TypeReferenceDirectiveResolutionInfo | undefined): readonly (ResolvedTypeReferenceDirective | undefined)[] { if (!typeDirectiveNames.length) return []; const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile; const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined; const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined; tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName }); performance.mark("beforeResolveTypeReference"); - const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference, containingFileMode); + const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference, containingFileMode, resolutionInfo); performance.mark("afterResolveTypeReference"); performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference"); tracing?.pop(); @@ -1759,6 +1765,96 @@ 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)[] { + 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); + } + + const oldSourceFile = !isString(containingFile) ? oldProgram && oldProgram.getSourceFile(containingFile.fileName) : undefined; + if (!isString(containingFile)) { + if (oldSourceFile !== containingFile && containingFile.resolvedTypeReferenceDirectiveNames) { + // `file` was created for the new program. + // + // We only set `file.resolvedTypeReferenceDirectiveNames` via work from the current function, + // so it is defined iff we already called the current function on `file`. + // That call happened no later than the creation of the `file` object, + // 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)[] = []; + 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); + result.push(resolvedTypeReferenceDirective); + } + return result; + } + } + + /** 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; + const containingSourceFile = !isString(containingFile) ? containingFile : undefined; + const canReuseResolutions = !isString(containingFile) ? + containingFile === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path) : + !hasInvalidatedResolutions(toPath(containingFile)); + for (let i = 0; i < typeDirectiveNames.length; i++) { + const entry = typeDirectiveNames[i]; + if (canReuseResolutions) { + const typeDirectiveName = getResolutionName(entry); + const mode = getModeForFileReference(entry, containingSourceFile?.impliedNodeFormat); + const oldResolvedTypeReferenceDirective = getResolvedTypeReferenceDirective(oldSourceFile, typeDirectiveName, mode); + if (oldResolvedTypeReferenceDirective) { + if (isTraceEnabled(options, host)) { + trace(host, + oldResolvedTypeReferenceDirective.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) + ); + } + (result ??= new Array(typeDirectiveNames.length))[i] = oldResolvedTypeReferenceDirective; + (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); + } + + if (!unknownTypeReferenceDirectiveNames) return result || emptyArray; + const resolutions = resolveTypeReferenceDirectiveNamesWorker( + unknownTypeReferenceDirectiveNames, + containingFile, + { names: unknownTypeReferenceDirectiveNames, reusedNames } + ); + + // Combine results of resolutions + if (!result) { + // There were no unresolved resolutions. + Debug.assert(resolutions.length === typeDirectiveNames.length); + return resolutions; + } + + let j = 0; + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + result[i] = resolutions[j]; + j++; + } + } + Debug.assert(j === resolutions.length); + return result; + } + function canReuseProjectReferences(): boolean { return !forEachProjectReference( oldProgram!.getProjectReferences(), @@ -1962,7 +2058,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg newSourceFile.resolvedModules = oldSourceFile.resolvedModules; } const typesReferenceDirectives = newSourceFile.typeReferenceDirectives; - const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile); + const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typesReferenceDirectives, newSourceFile); // ensure that types resolutions are still correct const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, newSourceFile, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo); if (typeReferenceResolutionsChanged) { @@ -3229,13 +3325,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return; } - const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file); + const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file); for (let index = 0; index < typeDirectives.length; index++) { const ref = file.typeReferenceDirectives[index]; const resolvedTypeReferenceDirective = resolutions[index]; // store resolved type directive on the file const fileName = toFileNameLowerCase(ref.fileName); - setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective); + 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)); diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 44ed620cf23bc..2e52acb3cc02c 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -15,7 +15,7 @@ import { ReadonlyESMap, removeSuffix, removeTrailingDirectorySeparator, resolutionExtensionIsTSOrJson, ResolvedModuleFull, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, returnTrue, Set, some, SourceFile, startsWith, - stringContains, trace, unorderedRemoveItem, WatchDirectoryFlags, + stringContains, trace, TypeReferenceDirectiveResolutionInfo, unorderedRemoveItem, WatchDirectoryFlags, } from "./_namespaces/ts"; /** @@ -36,7 +36,13 @@ export interface ResolutionCache { resolutionInfo: ModuleResolutionInfo | undefined ): (ResolvedModuleFull | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined; - resolveTypeReferenceDirectives(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"]): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives( + typeDirectiveNames: string[] | readonly FileReference[], + containingFile: string, + redirectedReference: ResolvedProjectReference | undefined, + containingFileMode: SourceFile["impliedNodeFormat"] | undefined, + resolutionInfo: TypeReferenceDirectiveResolutionInfo | undefined, + ): (ResolvedTypeReferenceDirective | undefined)[]; invalidateResolutionsOfFailedLookupLocations(): boolean; invalidateResolutionOfFile(filePath: Path): void; @@ -445,7 +451,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName; shouldRetryResolution: (t: T) => boolean; reusedNames?: readonly string[]; - resolutionInfo?: ModuleResolutionInfo; + resolutionInfo?: ModuleResolutionInfo | TypeReferenceDirectiveResolutionInfo; logChanges?: boolean; containingSourceFile?: SourceFile; containingSourceFileMode?: SourceFile["impliedNodeFormat"]; @@ -479,7 +485,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD const seenNamesInFile = createModeAwareCache(); let i = 0; for (const entry of containingSourceFile && resolutionInfo ? resolutionInfo.names : names) { - const name = !isString(entry) ? getResolutionName(entry) : entry; + 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. @@ -576,7 +582,13 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD } if (containingSourceFile && resolutionInfo) { - resolutionInfo.reusedNames?.forEach(literal => seenNamesInFile.set(literal.text, getModeForUsageLocation(containingSourceFile, literal), true)); + resolutionInfo.reusedNames?.forEach(entry => seenNamesInFile.set( + getResolutionName(entry), + !isString(entry) && isStringLiteralLike(entry) ? + getModeForUsageLocation(containingSourceFile, entry) : + getModeForFileReference(entry, containingSourceFile.impliedNodeFormat), + true, + )); reusedNames = undefined; } if (resolutionsInFile.size() !== seenNamesInFile.size()) { @@ -610,7 +622,13 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD } } - function resolveTypeReferenceDirectives(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"]): (ResolvedTypeReferenceDirective | undefined)[] { + function resolveTypeReferenceDirectives( + typeDirectiveNames: string[] | readonly FileReference[], + containingFile: string, + redirectedReference?: ResolvedProjectReference, + containingFileMode?: SourceFile["impliedNodeFormat"], + resolutionInfo?: TypeReferenceDirectiveResolutionInfo, + ): (ResolvedTypeReferenceDirective | undefined)[] { return resolveNamesWithLocalCache({ names: typeDirectiveNames, containingFile, @@ -620,7 +638,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD loader: resolveTypeReferenceDirective, getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective, shouldRetryResolution: resolution => resolution.resolvedTypeReferenceDirective === undefined, - containingSourceFileMode: containingFileMode + containingSourceFileMode: containingFileMode, + resolutionInfo, }); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fa193dacc1244..b754fb57f944f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7291,10 +7291,12 @@ export type HasInvalidatedResolutions = (sourceFile: Path) => boolean; /** @internal */ export type HasChangedAutomaticTypeDirectiveNames = () => boolean; -export interface ModuleResolutionInfo { - names: readonly StringLiteralLike[]; - reusedNames: readonly StringLiteralLike[] | undefined; +export interface ResolutionInfo { + names: readonly T[]; + reusedNames: readonly T[] | undefined; } +export type ModuleResolutionInfo = ResolutionInfo; +export type TypeReferenceDirectiveResolutionInfo = ResolutionInfo; export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; @@ -7324,7 +7326,7 @@ export interface CompilerHost extends ModuleResolutionHost { /** * 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?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string | undefined; /** @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void; /** @internal */ onReleaseParsedCommandLine?(configFileName: string, oldResolvedRef: ResolvedProjectReference | undefined, optionOptions: CompilerOptions): void; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 50dd570c8a8fd..6991c53f5693b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -291,12 +291,17 @@ export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string } /** @internal */ -export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective): void { +export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): void { if (!sourceFile.resolvedTypeReferenceDirectiveNames) { sourceFile.resolvedTypeReferenceDirectiveNames = createModeAwareCache(); } - sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, /*mode*/ undefined, resolvedTypeReferenceDirective); + sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, mode, resolvedTypeReferenceDirective); +} + +/** @internal */ +export function getResolvedTypeReferenceDirective(sourceFile: SourceFile | undefined, typeReferenceDirectiveName: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): ResolvedTypeReferenceDirective | undefined { + return sourceFile?.resolvedTypeReferenceDirectiveNames?.get(typeReferenceDirectiveName, mode); } /** @internal */ diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 6762477e51c16..14291500f83ae 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -17,6 +17,7 @@ import { ParsedCommandLine, Path, perfLogger, PollingInterval, ProjectReference, ResolutionCacheHost, ResolvedModule, ResolvedProjectReference, ResolvedTypeReferenceDirective, returnFalse, returnTrue, ScriptTarget, setGetSourceFileAsHashVersioned, SharedExtendedConfigFileWatcher, SourceFile, sys, System, toPath, + TypeReferenceDirectiveResolutionInfo, updateErrorForNoInputFiles, updateMissingFilePathsWatch, updateSharedExtendedConfigFileWatcher, updateWatchingWildcardDirectories, version, WatchDirectoryFlags, WatchOptions, WatchType, WatchTypeRegistry, WildcardDirectoryWatcher, @@ -134,7 +135,7 @@ export interface ProgramHost { /** 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?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; /** 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; /** @@ -397,7 +398,7 @@ export function createWatchProgram(host: WatchCompiler ((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) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode)); + ((typeDirectiveNames, containingFile, redirectedReference, _options, containingFileMode, resolutionInfo) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode, resolutionInfo)); compilerHost.getModuleResolutionCache = host.resolveModuleNames ? maybeBind(host, host.getModuleResolutionCache) : (() => resolutionCache.getModuleResolutionCache()); diff --git a/src/server/project.ts b/src/server/project.ts index 1494528726bfe..64bd6a6c5c7c9 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -27,7 +27,7 @@ import { ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, resolvePackageNameToPackageJson, returnFalse, returnTrue, ScriptKind, Set, some, sort, sortAndDeduplicate, SortedReadonlyArray, SourceFile, SourceMapper, startsWith, stripQuotes, StructureIsReused, SymlinkCache, - ThrottledCancellationToken, timestamp, toPath, tracing, TypeAcquisition, updateErrorForNoInputFiles, + ThrottledCancellationToken, timestamp, toPath, tracing, TypeAcquisition, TypeReferenceDirectiveResolutionInfo, updateErrorForNoInputFiles, updateMissingFilePathsWatch, WatchDirectoryFlags, WatchOptions, WatchType, } from "./_namespaces/ts"; @@ -554,8 +554,8 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo return this.resolutionCache.getResolvedModuleWithFailedLookupLocationsFromCache(moduleName, containingFile, resolutionMode); } - resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[] { - return this.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode); + resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[] { + return this.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode, resolutionInfo); } directoryExists(path: string): boolean { diff --git a/src/services/types.ts b/src/services/types.ts index 044ac4132caee..e27f3967471b0 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -5,7 +5,7 @@ import { MinimalResolutionCacheHost, ModuleKind, ModuleResolutionCache, ModuleResolutionInfo, ModuleSpecifierCache, ParsedCommandLine, Path, Program, ProjectReference, ResolvedModule, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, ScriptKind, Set, SourceFile, SourceFileLike, SourceMapper, - Symbol, SymlinkCache, TextChangeRange, textChanges, TextRange, TextSpan, UserPreferences, + Symbol, SymlinkCache, TextChangeRange, textChanges, TextRange, TextSpan, TypeReferenceDirectiveResolutionInfo, UserPreferences, } from "./_namespaces/ts"; declare module "../compiler/types" { @@ -325,7 +325,7 @@ export interface LanguageServiceHost extends GetEffectiveTypeRootsHost, MinimalR */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; - resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; /** @internal */ hasInvalidatedResolutions?: HasInvalidatedResolutions; /** @internal */ hasChangedAutomaticTypeDirectiveNames?: HasChangedAutomaticTypeDirectiveNames; /** @internal */ getGlobalTypingsCacheLocation?(): string | undefined; diff --git a/src/testRunner/unittests/reuseProgramStructure.ts b/src/testRunner/unittests/reuseProgramStructure.ts index 28fd3300f5502..95daa58cef639 100644 --- a/src/testRunner/unittests/reuseProgramStructure.ts +++ b/src/testRunner/unittests/reuseProgramStructure.ts @@ -483,11 +483,7 @@ describe("unittests:: Reuse program structure:: General", () => { "Explicitly specified module resolution kind: 'Classic'.", "File 'b1.ts' exist - use it as a name resolution result.", "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", - "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", - "Resolving with primary search path 'node_modules/@types'.", - "File 'node_modules/@types/typerefs2/package.json' does not exist.", - "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", - "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", + "Reusing resolution of type reference directive 'typerefs2' from 'f2.ts' of old program, it was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts'.", "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'." ], "program2: reuse module resolutions in f2 since it is unchanged"); @@ -507,11 +503,7 @@ describe("unittests:: Reuse program structure:: General", () => { "Explicitly specified module resolution kind: 'Classic'.", "File 'b1.ts' exist - use it as a name resolution result.", "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", - "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", - "Resolving with primary search path 'node_modules/@types'.", - "File 'node_modules/@types/typerefs2/package.json' does not exist.", - "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", - "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", + "Reusing resolution of type reference directive 'typerefs2' from 'f2.ts' of old program, it was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts'.", "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'." ], "program3: reuse module resolutions in f2 since it is unchanged"); @@ -532,11 +524,7 @@ describe("unittests:: Reuse program structure:: General", () => { "Explicitly specified module resolution kind: 'Classic'.", "File 'b1.ts' exist - use it as a name resolution result.", "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", - "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", - "Resolving with primary search path 'node_modules/@types'.", - "File 'node_modules/@types/typerefs2/package.json' does not exist.", - "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", - "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", + "Reusing resolution of type reference directive 'typerefs2' from 'f2.ts' of old program, it was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts'.", "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'.", ], "program_4: reuse module resolutions in f2 since it is unchanged"); @@ -573,11 +561,7 @@ describe("unittests:: Reuse program structure:: General", () => { "Explicitly specified module resolution kind: 'Classic'.", "File 'b1.ts' exist - use it as a name resolution result.", "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", - "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", - "Resolving with primary search path 'node_modules/@types'.", - "File 'node_modules/@types/typerefs2/package.json' does not exist.", - "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", - "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", + "Reusing resolution of type reference directive 'typerefs2' from 'f2.ts' of old program, it was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts'.", "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'.", ], "program_6: reuse module resolutions in f2 since it is unchanged"); @@ -593,11 +577,7 @@ describe("unittests:: Reuse program structure:: General", () => { assert.lengthOf(program7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`); assert.deepEqual(program7.host.getTrace(), [ - "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", - "Resolving with primary search path 'node_modules/@types'.", - "File 'node_modules/@types/typerefs2/package.json' does not exist.", - "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", - "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", + "Reusing resolution of type reference directive 'typerefs2' from 'f2.ts' of old program, it was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts'.", "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'.", ], "program_7 should reuse module resolutions in f2 since it is unchanged"); diff --git a/src/testRunner/unittests/tscWatch/moduleResolution.ts b/src/testRunner/unittests/tscWatch/moduleResolution.ts index a03b0f8750824..ad5894ec250ba 100644 --- a/src/testRunner/unittests/tscWatch/moduleResolution.ts +++ b/src/testRunner/unittests/tscWatch/moduleResolution.ts @@ -328,4 +328,93 @@ describe("unittests:: tsc-watch:: moduleResolution", () => { } ] }); + + ts.tscWatch.verifyTscWatch({ + scenario: "moduleResolution", + subScenario: "type reference resolutions reuse", + sys: () => ts.tscWatch.createWatchedSystem([ + { + path: `${ts.tscWatch.projectRoot}/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { moduleResolution: "node16" }, + }) + }, + { + path: `${ts.tscWatch.projectRoot}/index.ts`, + content: Utils.dedent` + /// + /// + export interface LocalInterface extends RequireInterface {} + ` + }, + { + path: `${ts.tscWatch.projectRoot}/a.ts`, + content: Utils.dedent` + export const x = 10; + ` + }, + { + path: `${ts.tscWatch.projectRoot}/node_modules/pkg/package.json`, + content: JSON.stringify({ + name: "pkg", + version: "0.0.1", + exports: { + import: "./import.js", + require: "./require.js" + } + }) + }, + { + path: `${ts.tscWatch.projectRoot}/node_modules/pkg/import.d.ts`, + content: Utils.dedent` + export {}; + declare global { + interface ImportInterface {} + } + ` + }, + { + path: `${ts.tscWatch.projectRoot}/node_modules/pkg/require.d.ts`, + content: Utils.dedent` + export {}; + declare global { + interface RequireInterface {} + } + ` + }, + { + path: `${ts.tscWatch.projectRoot}/node_modules/pkg1/package.json`, + content: JSON.stringify({ + name: "pkg1", + version: "0.0.1", + exports: { + import: "./import.js", + require: "./require.js" + } + }) + }, + { + path: `${ts.tscWatch.projectRoot}/node_modules/pkg1/import.d.ts`, + content: Utils.dedent` + export {}; + declare global { + interface ImportInterface {} + } + ` + }, + { + path: `${ts.tscWatch.projectRoot}/node_modules/@types/pkg2/index.d.ts`, + content: `export const x = 10;` + }, + ts.tscWatch.libFile + ], { currentDirectory: ts.tscWatch.projectRoot }), + commandLineArgs: ["-w", "--traceResolution"], + changes: [ + { + caption: "modify aFile by adding import", + change: sys => sys.prependFile(`${ts.tscWatch.projectRoot}/a.ts`, `/// \n`), + timeouts: sys => sys.runQueuedTimeoutCallbacks(), + } + ] + }); }); \ No newline at end of file diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ce1759035fc38..6a93d33726e1b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3187,7 +3187,7 @@ declare namespace ts { resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModuleFull | undefined)[]; getModuleResolutionCache(): ModuleResolutionCache | undefined; getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; - resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; directoryExists(path: string): boolean; getDirectories(path: string): string[]; log(s: string): void; @@ -7378,10 +7378,12 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; readonly failedLookupLocations: string[]; } - interface ModuleResolutionInfo { - names: readonly StringLiteralLike[]; - reusedNames: readonly StringLiteralLike[] | undefined; + interface ResolutionInfo { + names: readonly T[]; + reusedNames: readonly T[] | undefined; } + type ModuleResolutionInfo = ResolutionInfo; + type TypeReferenceDirectiveResolutionInfo = ResolutionInfo; 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; @@ -7402,7 +7404,7 @@ declare namespace ts { /** * 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?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string | undefined; /** 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; @@ -9676,7 +9678,7 @@ declare namespace ts { /** 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?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; /** 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; /** @@ -9910,7 +9912,7 @@ declare namespace ts { getTypeRootsVersion?(): number; resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; - resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; getDirectories?(directoryName: string): string[]; /** * Gets a set of custom transformers to use during emit. diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 4c59e90fd71e2..cc0b9416ac78e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3440,10 +3440,12 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; readonly failedLookupLocations: string[]; } - interface ModuleResolutionInfo { - names: readonly StringLiteralLike[]; - reusedNames: readonly StringLiteralLike[] | undefined; + interface ResolutionInfo { + names: readonly T[]; + reusedNames: readonly T[] | undefined; } + type ModuleResolutionInfo = ResolutionInfo; + type TypeReferenceDirectiveResolutionInfo = ResolutionInfo; 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; @@ -3464,7 +3466,7 @@ declare namespace ts { /** * 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?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string | undefined; /** 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; @@ -5738,7 +5740,7 @@ declare namespace ts { /** 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?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; /** 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; /** @@ -6045,7 +6047,7 @@ declare namespace ts { getTypeRootsVersion?(): number; resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile, resolutionInfo?: ModuleResolutionInfo): (ResolvedModule | undefined)[]; getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined; - resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; + resolveTypeReferenceDirectives?(typeDirectiveNames: string[] | FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined, resolutionInfo?: TypeReferenceDirectiveResolutionInfo): (ResolvedTypeReferenceDirective | undefined)[]; getDirectories?(directoryName: string): string[]; /** * Gets a set of custom transformers to use during emit. diff --git a/tests/baselines/reference/tscWatch/moduleResolution/type-reference-resolutions-reuse.js b/tests/baselines/reference/tscWatch/moduleResolution/type-reference-resolutions-reuse.js new file mode 100644 index 0000000000000..30637c5340476 --- /dev/null +++ b/tests/baselines/reference/tscWatch/moduleResolution/type-reference-resolutions-reuse.js @@ -0,0 +1,359 @@ +Input:: +//// [/user/username/projects/myproject/tsconfig.json] +{"compilerOptions":{"moduleResolution":"node16"}} + +//// [/user/username/projects/myproject/index.ts] +/// +/// +export interface LocalInterface extends RequireInterface {} + + +//// [/user/username/projects/myproject/a.ts] +export const x = 10; + + +//// [/user/username/projects/myproject/node_modules/pkg/package.json] +{"name":"pkg","version":"0.0.1","exports":{"import":"./import.js","require":"./require.js"}} + +//// [/user/username/projects/myproject/node_modules/pkg/import.d.ts] +export {}; +declare global { + interface ImportInterface {} +} + + +//// [/user/username/projects/myproject/node_modules/pkg/require.d.ts] +export {}; +declare global { + interface RequireInterface {} +} + + +//// [/user/username/projects/myproject/node_modules/pkg1/package.json] +{"name":"pkg1","version":"0.0.1","exports":{"import":"./import.js","require":"./require.js"}} + +//// [/user/username/projects/myproject/node_modules/pkg1/import.d.ts] +export {}; +declare global { + interface ImportInterface {} +} + + +//// [/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts] +export const x = 10; + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + + +/a/lib/tsc.js -w --traceResolution +Output:: +>> Screen clear +[12:00:45 AM] Starting compilation in watch mode... + +File '/user/username/projects/myproject/package.json' does not exist. +File '/user/username/projects/package.json' does not exist. +File '/user/username/package.json' does not exist. +File '/user/package.json' does not exist. +File '/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +======== Resolving type reference directive 'pkg', containing file '/user/username/projects/myproject/index.ts', root directory '/user/username/projects/myproject/node_modules/@types'. ======== +Resolving with primary search path '/user/username/projects/myproject/node_modules/@types'. +Looking up in 'node_modules' folder, initial location '/user/username/projects/myproject'. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg/package.json'. +'package.json' does not have a 'typesVersions' field. +Matched 'exports' condition 'import'. +Using 'exports' subpath '.' with target './import.js'. +File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exist - use it as a name resolution result. +Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'. +======== Type reference directive 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1', primary: false. ======== +======== Resolving type reference directive 'pkg1', containing file '/user/username/projects/myproject/index.ts', root directory '/user/username/projects/myproject/node_modules/@types'. ======== +Resolving with primary search path '/user/username/projects/myproject/node_modules/@types'. +Looking up in 'node_modules' folder, initial location '/user/username/projects/myproject'. +Found 'package.json' at '/user/username/projects/myproject/node_modules/pkg1/package.json'. +'package.json' does not have a 'typesVersions' field. +Saw non-matching condition 'import'. +Matched 'exports' condition 'require'. +Using 'exports' subpath '.' with target './require.js'. +File name '/user/username/projects/myproject/node_modules/pkg1/require.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg1/require.d.ts' does not exist. +File '/user/username/projects/myproject/node_modules/@types/pkg1.d.ts' does not exist. +Directory '/user/username/projects/node_modules' does not exist, skipping all lookups in it. +Directory '/user/username/node_modules' does not exist, skipping all lookups in it. +Directory '/user/node_modules' does not exist, skipping all lookups in it. +Directory '/node_modules' does not exist, skipping all lookups in it. +======== Type reference directive 'pkg1' was not resolved. ======== +File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups. +======== Resolving type reference directive 'pkg2', containing file '/user/username/projects/myproject/__inferred type names__.ts', root directory '/user/username/projects/myproject/node_modules/@types'. ======== +Resolving with primary search path '/user/username/projects/myproject/node_modules/@types'. +File '/user/username/projects/myproject/node_modules/@types/pkg2/package.json' does not exist. +File '/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts' exist - use it as a name resolution result. +Resolving real path for '/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts', result '/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts'. +======== Type reference directive 'pkg2' was successfully resolved to '/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts', primary: true. ======== +File '/user/username/projects/myproject/node_modules/@types/pkg2/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/@types/package.json' does not exist. +File '/user/username/projects/myproject/node_modules/package.json' does not exist. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +File '/a/lib/package.json' does not exist. +File '/a/package.json' does not exist. +File '/package.json' does not exist according to earlier cached lookups. +index.ts:2:23 - error TS2688: Cannot find type definition file for 'pkg1'. + +2 /// +   ~~~~ + +index.ts:3:41 - error TS2304: Cannot find name 'RequireInterface'. + +3 export interface LocalInterface extends RequireInterface {} +   ~~~~~~~~~~~~~~~~ + +[12:00:50 AM] Found 2 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/a.ts","/user/username/projects/myproject/index.ts"] +Program options: {"moduleResolution":3,"watch":true,"traceResolution":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/node_modules/pkg/import.d.ts +/user/username/projects/myproject/index.ts +/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts + +Semantic diagnostics in builder refreshed for:: +/a/lib/lib.d.ts +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/node_modules/pkg/import.d.ts +/user/username/projects/myproject/index.ts +/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts + +Shape signatures in builder refreshed for:: +/a/lib/lib.d.ts (used version) +/user/username/projects/myproject/a.ts (used version) +/user/username/projects/myproject/node_modules/pkg/import.d.ts (used version) +/user/username/projects/myproject/index.ts (used version) +/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts (used version) + +PolledWatches:: +/user/username/projects/myproject/package.json: + {"pollingInterval":2000} +/user/username/projects/package.json: + {"pollingInterval":2000} +/user/username/projects/myproject/node_modules/@types/pkg2/package.json: + {"pollingInterval":2000} +/user/username/projects/myproject/node_modules/@types/package.json: + {"pollingInterval":2000} +/user/username/projects/myproject/node_modules/package.json: + {"pollingInterval":2000} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/user/username/projects/myproject/a.ts: + {} +/user/username/projects/myproject/index.ts: + {} +/user/username/projects/myproject/node_modules/pkg/import.d.ts: + {} +/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts: + {} +/a/lib/lib.d.ts: + {} +/user/username/projects/myproject/node_modules/pkg/package.json: + {} +/user/username/projects/myproject/node_modules/pkg1/package.json: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject/node_modules: + {} +/user/username/projects/myproject/node_modules/@types: + {} +/user/username/projects/myproject: + {} + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/a.js] +"use strict"; +exports.__esModule = true; +exports.x = void 0; +exports.x = 10; + + +//// [/user/username/projects/myproject/index.js] +"use strict"; +exports.__esModule = true; +/// +/// + + + +Change:: modify aFile by adding import + +Input:: +//// [/user/username/projects/myproject/a.ts] +/// +export const x = 10; + + + +Output:: +>> Screen clear +[12:00:53 AM] File change detected. Starting incremental compilation... + +File '/a/lib/package.json' does not exist according to earlier cached lookups. +File '/a/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/@types/pkg2/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/@types/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +======== Resolving type reference directive 'pkg', containing file '/user/username/projects/myproject/a.ts', root directory '/user/username/projects/myproject/node_modules/@types'. ======== +Resolving with primary search path '/user/username/projects/myproject/node_modules/@types'. +Looking up in 'node_modules' folder, initial location '/user/username/projects/myproject'. +File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups. +Matched 'exports' condition 'import'. +Using 'exports' subpath '.' with target './import.js'. +File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it. +File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exist - use it as a name resolution result. +Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'. +======== Type reference directive 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1', primary: false. ======== +File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +Reusing resolution of type reference directive 'pkg' from '/user/username/projects/myproject/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. +Reusing resolution of type reference directive 'pkg1' from '/user/username/projects/myproject/index.ts' of old program, it was not resolved. +Reusing resolution of type reference directive 'pkg2' from '/user/username/projects/myproject/__inferred type names__.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts'. +File '/user/username/projects/myproject/node_modules/@types/pkg2/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/@types/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/node_modules/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups. +File '/user/username/projects/package.json' does not exist according to earlier cached lookups. +File '/user/username/package.json' does not exist according to earlier cached lookups. +File '/user/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +File '/a/lib/package.json' does not exist according to earlier cached lookups. +File '/a/package.json' does not exist according to earlier cached lookups. +File '/package.json' does not exist according to earlier cached lookups. +index.ts:2:23 - error TS2688: Cannot find type definition file for 'pkg1'. + +2 /// +   ~~~~ + +index.ts:3:41 - error TS2304: Cannot find name 'RequireInterface'. + +3 export interface LocalInterface extends RequireInterface {} +   ~~~~~~~~~~~~~~~~ + +[12:00:57 AM] Found 2 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/a.ts","/user/username/projects/myproject/index.ts"] +Program options: {"moduleResolution":3,"watch":true,"traceResolution":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program structureReused: SafeModules +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/node_modules/pkg/import.d.ts +/user/username/projects/myproject/a.ts +/user/username/projects/myproject/index.ts +/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts + +Semantic diagnostics in builder refreshed for:: +/user/username/projects/myproject/a.ts + +Shape signatures in builder refreshed for:: +/user/username/projects/myproject/a.ts (computed .d.ts) + +PolledWatches:: +/user/username/projects/myproject/package.json: + {"pollingInterval":2000} +/user/username/projects/package.json: + {"pollingInterval":2000} +/user/username/projects/myproject/node_modules/@types/pkg2/package.json: + {"pollingInterval":2000} +/user/username/projects/myproject/node_modules/@types/package.json: + {"pollingInterval":2000} +/user/username/projects/myproject/node_modules/package.json: + {"pollingInterval":2000} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/user/username/projects/myproject/a.ts: + {} +/user/username/projects/myproject/index.ts: + {} +/user/username/projects/myproject/node_modules/pkg/import.d.ts: + {} +/user/username/projects/myproject/node_modules/@types/pkg2/index.d.ts: + {} +/a/lib/lib.d.ts: + {} +/user/username/projects/myproject/node_modules/pkg/package.json: + {} +/user/username/projects/myproject/node_modules/pkg1/package.json: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject/node_modules: + {} +/user/username/projects/myproject/node_modules/@types: + {} +/user/username/projects/myproject: + {} + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/a.js] +"use strict"; +exports.__esModule = true; +exports.x = void 0; +/// +exports.x = 10; + +