Skip to content

Commit 91bb32a

Browse files
authored
Merge pull request #27560 from Microsoft/transitiveReferences
Resolve modules, type reference directives in context of referenced file
2 parents e58371e + bc72577 commit 91bb32a

25 files changed

+1150
-379
lines changed

src/compiler/commandLineParser.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,9 @@ namespace ts {
12931293

12941294
const result = parseJsonText(configFileName, configFileText);
12951295
const cwd = host.getCurrentDirectory();
1296+
result.path = toPath(configFileName, cwd, createGetCanonicalFileName(host.useCaseSensitiveFileNames));
1297+
result.resolvedPath = result.path;
1298+
result.originalFileName = result.fileName;
12961299
return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
12971300
}
12981301

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3763,6 +3763,10 @@
37633763
"category": "Message",
37643764
"code": 6214
37653765
},
3766+
"Using compiler options of project reference redirect '{0}'.": {
3767+
"category": "Message",
3768+
"code": 6215
3769+
},
37663770

37673771
"Projects to reference": {
37683772
"category": "Message",

src/compiler/moduleNameResolver.ts

+88-38
Large diffs are not rendered by default.

src/compiler/program.ts

+245-135
Large diffs are not rendered by default.

src/compiler/resolutionCache.ts

+43-17
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ namespace ts {
55
startRecordingFilesWithChangedResolutions(): void;
66
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
77

8-
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[];
8+
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): ResolvedModuleFull[];
99
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined;
10-
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
10+
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[];
1111

1212
invalidateResolutionOfFile(filePath: Path): void;
1313
removeResolutionsOfFile(filePath: Path): void;
14+
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
1415
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map<ReadonlyArray<string>>): void;
1516
createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution;
1617

@@ -90,17 +91,17 @@ namespace ts {
9091
// The key in the map is source file's path.
9192
// The values are Map of resolutions with key being name lookedup.
9293
const resolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
93-
const perDirectoryResolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
94-
const nonRelaticeModuleNameCache = createMap<PerModuleNameCache>();
94+
const perDirectoryResolvedModuleNames: CacheWithRedirects<Map<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
95+
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
9596
const moduleResolutionCache = createModuleResolutionCacheWithMaps(
9697
perDirectoryResolvedModuleNames,
97-
nonRelaticeModuleNameCache,
98+
nonRelativeModuleNameCache,
9899
getCurrentDirectory(),
99100
resolutionHost.getCanonicalFileName
100101
);
101102

102103
const resolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
103-
const perDirectoryResolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
104+
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
104105

105106
/**
106107
* These are the extensions that failed lookup files will have by default,
@@ -128,6 +129,7 @@ namespace ts {
128129
resolveModuleNames,
129130
getResolvedModuleWithFailedLookupLocationsFromCache,
130131
resolveTypeReferenceDirectives,
132+
removeResolutionsFromProjectReferenceRedirects,
131133
removeResolutionsOfFile,
132134
invalidateResolutionOfFile,
133135
setFilesWithInvalidatedNonRelativeUnresolvedImports,
@@ -199,7 +201,7 @@ namespace ts {
199201

200202
function clearPerDirectoryResolutions() {
201203
perDirectoryResolvedModuleNames.clear();
202-
nonRelaticeModuleNameCache.clear();
204+
nonRelativeModuleNameCache.clear();
203205
perDirectoryResolvedTypeReferenceDirectives.clear();
204206
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
205207
nonRelativeExternalModuleResolutions.clear();
@@ -217,8 +219,8 @@ namespace ts {
217219
});
218220
}
219221

220-
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): CachedResolvedModuleWithFailedLookupLocations {
221-
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache);
222+
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedModuleWithFailedLookupLocations {
223+
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference);
222224
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
223225
if (!resolutionHost.getGlobalCache) {
224226
return primaryResult;
@@ -242,16 +244,18 @@ namespace ts {
242244
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
243245
names: string[],
244246
containingFile: string,
247+
redirectedReference: ResolvedProjectReference | undefined,
245248
cache: Map<Map<T>>,
246-
perDirectoryCache: Map<Map<T>>,
247-
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
249+
perDirectoryCacheWithRedirects: CacheWithRedirects<Map<T>>,
250+
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T,
248251
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
249252
reusedNames: string[] | undefined,
250253
logChanges: boolean): R[] {
251254

252255
const path = resolutionHost.toPath(containingFile);
253256
const resolutionsInFile = cache.get(path) || cache.set(path, createMap()).get(path)!;
254257
const dirPath = getDirectoryPath(path);
258+
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
255259
let perDirectoryResolution = perDirectoryCache.get(dirPath);
256260
if (!perDirectoryResolution) {
257261
perDirectoryResolution = createMap();
@@ -260,12 +264,20 @@ namespace ts {
260264
const resolvedModules: R[] = [];
261265
const compilerOptions = resolutionHost.getCompilationSettings();
262266
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
267+
268+
// All the resolutions in this file are invalidated if this file wasnt resolved using same redirect
269+
const program = resolutionHost.getCurrentProgram();
270+
const oldRedirect = program && program.getResolvedProjectReferenceToRedirect(containingFile);
271+
const unmatchedRedirects = oldRedirect ?
272+
!redirectedReference || redirectedReference.sourceFile.path !== oldRedirect.sourceFile.path :
273+
!!redirectedReference;
274+
263275
const seenNamesInFile = createMap<true>();
264276
for (const name of names) {
265277
let resolution = resolutionsInFile.get(name);
266278
// Resolution is valid if it is present and not invalidated
267279
if (!seenNamesInFile.has(name) &&
268-
allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated ||
280+
allFilesHaveInvalidatedResolution || unmatchedRedirects || !resolution || resolution.isInvalidated ||
269281
// If the name is unresolved import that was invalidated, recalculate
270282
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && !getResolutionWithResolvedFileName(resolution))) {
271283
const existingResolution = resolution;
@@ -274,7 +286,7 @@ namespace ts {
274286
resolution = resolutionInDirectory;
275287
}
276288
else {
277-
resolution = loader(name, containingFile, compilerOptions, resolutionHost);
289+
resolution = loader(name, containingFile, compilerOptions, resolutionHost, redirectedReference);
278290
perDirectoryResolution.set(name, resolution);
279291
}
280292
resolutionsInFile.set(name, resolution);
@@ -323,18 +335,18 @@ namespace ts {
323335
}
324336
}
325337

326-
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
338+
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[] {
327339
return resolveNamesWithLocalCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations, ResolvedTypeReferenceDirective>(
328-
typeDirectiveNames, containingFile,
340+
typeDirectiveNames, containingFile, redirectedReference,
329341
resolvedTypeReferenceDirectives, perDirectoryResolvedTypeReferenceDirectives,
330342
resolveTypeReferenceDirective, getResolvedTypeReferenceDirective,
331343
/*reusedNames*/ undefined, /*logChanges*/ false
332344
);
333345
}
334346

335-
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[] {
347+
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): ResolvedModuleFull[] {
336348
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>(
337-
moduleNames, containingFile,
349+
moduleNames, containingFile, redirectedReference,
338350
resolvedModuleNames, perDirectoryResolvedModuleNames,
339351
resolveModuleName, getResolvedModule,
340352
reusedNames, logChangesWhenResolvingModule
@@ -594,6 +606,20 @@ namespace ts {
594606
}
595607
}
596608

609+
function removeResolutionsFromProjectReferenceRedirects(filePath: Path) {
610+
if (!fileExtensionIs(filePath, Extension.Json)) { return; }
611+
612+
const program = resolutionHost.getCurrentProgram();
613+
if (!program) { return; }
614+
615+
// If this file is input file for the referenced project, get it
616+
const resolvedProjectReference = program.getResolvedProjectReferenceByPath(filePath);
617+
if (!resolvedProjectReference) { return; }
618+
619+
// filePath is for the projectReference and the containing file is from this project reference, invalidate the resolution
620+
resolvedProjectReference.commandLine.fileNames.forEach(f => removeResolutionsOfFile(resolutionHost.toPath(f)));
621+
}
622+
597623
function removeResolutionsOfFile(filePath: Path) {
598624
removeResolutionsOfFileFromCache(resolvedModuleNames, filePath);
599625
removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath);

src/compiler/types.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -2906,8 +2906,11 @@ namespace ts {
29062906
/* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
29072907

29082908
getProjectReferences(): ReadonlyArray<ProjectReference> | undefined;
2909-
getResolvedProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined;
2909+
getResolvedProjectReferences(): ReadonlyArray<ResolvedProjectReference | undefined> | undefined;
29102910
/*@internal*/ getProjectReferenceRedirect(fileName: string): string | undefined;
2911+
/*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
2912+
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
2913+
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
29112914
}
29122915

29132916
/* @internal */
@@ -2916,6 +2919,7 @@ namespace ts {
29162919
export interface ResolvedProjectReference {
29172920
commandLine: ParsedCommandLine;
29182921
sourceFile: SourceFile;
2922+
references?: ReadonlyArray<ResolvedProjectReference | undefined>;
29192923
}
29202924

29212925
/* @internal */
@@ -4957,13 +4961,13 @@ namespace ts {
49574961
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
49584962
* 'throw new Error("NotImplemented")'
49594963
*/
4960-
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[];
4964+
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference): (ResolvedModule | undefined)[];
49614965
/**
49624966
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
49634967
*/
4964-
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
4968+
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[];
49654969
getEnvironmentVariable?(name: string): string | undefined;
4966-
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void;
4970+
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void;
49674971
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
49684972
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
49694973
createHash?(data: string): string;

src/compiler/watch.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,9 @@ namespace ts {
338338
getEnvironmentVariable?(name: string): string | undefined;
339339

340340
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
341-
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
341+
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference): ResolvedModule[];
342342
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
343-
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
343+
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[];
344344
}
345345

346346
/** Internal interface used to wire emit through same host */
@@ -558,11 +558,11 @@ namespace ts {
558558
);
559559
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
560560
compilerHost.resolveModuleNames = host.resolveModuleNames ?
561-
((moduleNames, containingFile, reusedNames) => host.resolveModuleNames!(moduleNames, containingFile, reusedNames)) :
562-
((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames));
561+
((moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(moduleNames, containingFile, reusedNames, redirectedReference)) :
562+
((moduleNames, containingFile, reusedNames, redirectedReference) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference));
563563
compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ?
564-
((typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives!(typeDirectiveNames, containingFile)) :
565-
((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile));
564+
((typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(typeDirectiveNames, containingFile, redirectedReference)) :
565+
((typeDirectiveNames, containingFile, redirectedReference) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference));
566566
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
567567

568568
synchronizeProgram();
@@ -764,8 +764,8 @@ namespace ts {
764764
return !hostSourceFile || isFileMissingOnHost(hostSourceFile) ? undefined : hostSourceFile.version.toString();
765765
}
766766

767-
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) {
768-
const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.path);
767+
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) {
768+
const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.resolvedPath);
769769
// If this is the source file thats in the cache and new program doesnt need it,
770770
// remove the cached entry.
771771
// Note we arent deleting entry if file became missing in new program or
@@ -779,8 +779,10 @@ namespace ts {
779779
if ((hostSourceFileInfo as FilePresentOnHost).fileWatcher) {
780780
(hostSourceFileInfo as FilePresentOnHost).fileWatcher.close();
781781
}
782-
sourceFilesCache.delete(oldSourceFile.path);
783-
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
782+
sourceFilesCache.delete(oldSourceFile.resolvedPath);
783+
if (!hasSourceFileByPath) {
784+
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
785+
}
784786
}
785787
}
786788
}
@@ -875,6 +877,7 @@ namespace ts {
875877
if (eventKind === FileWatcherEventKind.Deleted && sourceFilesCache.get(path)) {
876878
resolutionCache.invalidateResolutionOfFile(path);
877879
}
880+
resolutionCache.removeResolutionsFromProjectReferenceRedirects(path);
878881
nextSourceFileVersion(path);
879882

880883
// Update the program

0 commit comments

Comments
 (0)