diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 46130e35659f9..0ec2b7ae90008 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -14,7 +14,7 @@ namespace ts { removeResolutionsOfFile(filePath: Path): void; removeResolutionsFromProjectReferenceRedirects(filePath: Path): void; setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: ESMap): void; - createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; + createHasInvalidatedResolution(userProvidedHasInvalidatedResolution?: HasInvalidatedResolution): HasInvalidatedResolution; hasChangedAutomaticTypeDirectiveNames(): boolean; isFileWithInvalidatedNonRelativeUnresolvedImports(path: Path): boolean; @@ -300,17 +300,13 @@ namespace ts { return !!value && !!value.length; } - function createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution { + function createHasInvalidatedResolution(userProvidedHasInvalidatedResolution?: HasInvalidatedResolution): HasInvalidatedResolution { // Ensure pending resolutions are applied invalidateResolutionsOfFailedLookupLocations(); - if (forceAllFilesAsInvalidated) { - // Any file asked would have invalidated resolution - filesWithInvalidatedResolutions = undefined; - return returnTrue; - } const collected = filesWithInvalidatedResolutions; filesWithInvalidatedResolutions = undefined; - return path => (!!collected && collected.has(path)) || + return path => userProvidedHasInvalidatedResolution?.(path) || + !!collected?.has(path) || isFileWithInvalidatedNonRelativeUnresolvedImports(path); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c9a3590a4dbfb..b18773af85b45 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7214,7 +7214,7 @@ namespace ts { getEnvironmentVariable?(name: string): string | undefined; /* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void; /* @internal */ onReleaseParsedCommandLine?(configFileName: string, oldResolvedRef: ResolvedProjectReference | undefined, optionOptions: CompilerOptions): void; - /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; + hasInvalidatedResolution?(filePath: Path): boolean; /* @internal */ hasChangedAutomaticTypeDirectiveNames?: HasChangedAutomaticTypeDirectiveNames; createHash?(data: string): string; getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined; diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 2f19e77aaf25a..b8fc6379d2fc7 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -112,7 +112,8 @@ namespace ts { resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (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)[]; - /*@internal*/ hasInvalidatedResolution?: HasInvalidatedResolution; + /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ + hasInvalidatedResolution?(filePath: Path): boolean; /** * 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 */ @@ -372,6 +373,7 @@ namespace ts { maybeBind(host, host.getModuleResolutionCache) : (() => resolutionCache.getModuleResolutionCache()); const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives; + const userProvidedHasInvalidatedResolution = maybeBind(host, host.hasInvalidatedResolution) || returnTrue; builderProgram = readBuilderProgram(compilerOptions, compilerHost) as any as T; synchronizeProgram(); @@ -445,7 +447,9 @@ namespace ts { } // All resolutions are invalid if user provided resolutions - const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution); + const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution( + userProvidedResolution ? userProvidedHasInvalidatedResolution : undefined + ); const { originalReadFile, originalFileExists, originalDirectoryExists, originalCreateDirectory, originalWriteFile, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 50fbd1d4dea72..b40a15f778716 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3320,6 +3320,7 @@ declare namespace ts { */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string | undefined; + hasInvalidatedResolution?(filePath: Path): boolean; createHash?(data: string): string; getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined; } @@ -5442,6 +5443,8 @@ declare namespace ts { resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (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)[]; + /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ + hasInvalidatedResolution?(filePath: Path): boolean; /** * 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 */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d323d77c229dd..947080d3de31d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3320,6 +3320,7 @@ declare namespace ts { */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string | undefined; + hasInvalidatedResolution?(filePath: Path): boolean; createHash?(data: string): string; getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined; } @@ -5442,6 +5443,8 @@ declare namespace ts { resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (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)[]; + /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ + hasInvalidatedResolution?(filePath: Path): boolean; /** * 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 */ diff --git a/tests/baselines/reference/tscWatch/watchApi/host-implements-hasInvalidatedResolution.js b/tests/baselines/reference/tscWatch/watchApi/host-implements-hasInvalidatedResolution.js index b18d7b8972d77..71dac5e657e1b 100644 --- a/tests/baselines/reference/tscWatch/watchApi/host-implements-hasInvalidatedResolution.js +++ b/tests/baselines/reference/tscWatch/watchApi/host-implements-hasInvalidatedResolution.js @@ -106,20 +106,13 @@ Synchronizing program CreatingProgramWith:: roots: ["/user/username/projects/myproject/main.ts"] options: {"traceResolution":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -======== Resolving module './other' from '/user/username/projects/myproject/main.ts'. ======== -Module resolution kind is not specified, using 'NodeJs'. -Loading module as file / folder, candidate module location '/user/username/projects/myproject/other', target file type 'TypeScript'. -File '/user/username/projects/myproject/other.ts' does not exist. -File '/user/username/projects/myproject/other.tsx' does not exist. -File '/user/username/projects/myproject/other.d.ts' exist - use it as a name resolution result. -======== Module name './other' was successfully resolved to '/user/username/projects/myproject/other.d.ts'. ======== [12:00:30 AM] Found 0 errors. Watching for file changes. Program root files: ["/user/username/projects/myproject/main.ts"] Program options: {"traceResolution":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -Program structureReused: SafeModules +Program structureReused: Completely Program files:: /a/lib/lib.d.ts /user/username/projects/myproject/other.d.ts @@ -165,20 +158,13 @@ Synchronizing program CreatingProgramWith:: roots: ["/user/username/projects/myproject/main.ts"] options: {"traceResolution":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -======== Resolving module './other' from '/user/username/projects/myproject/main.ts'. ======== -Module resolution kind is not specified, using 'NodeJs'. -Loading module as file / folder, candidate module location '/user/username/projects/myproject/other', target file type 'TypeScript'. -File '/user/username/projects/myproject/other.ts' does not exist. -File '/user/username/projects/myproject/other.tsx' does not exist. -File '/user/username/projects/myproject/other.d.ts' exist - use it as a name resolution result. -======== Module name './other' was successfully resolved to '/user/username/projects/myproject/other.d.ts'. ======== [12:00:37 AM] Found 0 errors. Watching for file changes. Program root files: ["/user/username/projects/myproject/main.ts"] Program options: {"traceResolution":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -Program structureReused: SafeModules +Program structureReused: Completely Program files:: /a/lib/lib.d.ts /user/username/projects/myproject/other.d.ts