Skip to content

Commit 95eeb32

Browse files
committed
Handle the difference is useSourceOfProjectReferenceRedirect and project reference files in the program so editor can use correct program
1 parent cd709a3 commit 95eeb32

5 files changed

+117
-52
lines changed

src/compiler/builder.ts

+16
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,15 @@ namespace ts {
8383
filesByName: ESMap<Path, SourceFileOfProgramFromBuildInfo | Path | typeof missingSourceOfProjectReferenceRedirect | typeof missingFile>;
8484
fileIncludeReasons: MultiMap<Path, FileIncludeReason>;
8585
sourceFileFromExternalLibraryPath: Set<Path> | undefined;
86+
sourceFileFromProjectReferencePath: Set<Path> | undefined;
8687
redirectTargetsMap: MultiMap<Path, string>;
8788
sourceFileToPackageName: ESMap<Path, string>;
8889
projectReferences: readonly ProjectReference[] | undefined;
8990
resolvedProjectReferences: readonly (ResolvedProjectReferenceOfProgramFromBuildInfo | undefined)[] | undefined;
9091
automaticTypeDirectiveNames: string[] | undefined;
9192
resolvedTypeReferenceDirectives: ESMap<string, ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
9293
fileProcessingDiagnostics: FilePreprocessingDiagnostic[] | undefined;
94+
useSourceOfProjectReferenceRedirect: boolean;
9395
}
9496

9597
export const enum BuilderFileEmit {
@@ -340,24 +342,28 @@ namespace ts {
340342
if (!state.program || !state.compilerOptions.persistResolutions || state.persistedProgramState) return;
341343
const filesByName = mapEntries(state.program.getFilesByNameMap(), (key, value) => [key, value ? value.path : value as SourceFileOfProgramFromBuildInfo | Path | typeof missingSourceOfProjectReferenceRedirect | typeof missingFile]);
342344
let sourceFileFromExternalLibraryPath: Set<Path> | undefined;
345+
let sourceFileFromProjectReferencePath: Set<Path> | undefined;
343346
const files = mapToReadonlyArray(state.program.getSourceFiles(), toSourceFileOfProgramFromBuildInfo);
344347
state.persistedProgramState = {
345348
files,
346349
rootFileNames: state.program.getRootFileNames(),
347350
filesByName,
348351
fileIncludeReasons: state.program.getFileIncludeReasons(),
349352
sourceFileFromExternalLibraryPath,
353+
sourceFileFromProjectReferencePath,
350354
redirectTargetsMap: state.program.redirectTargetsMap,
351355
sourceFileToPackageName: state.program.sourceFileToPackageName,
352356
projectReferences: state.program.getProjectReferences(),
353357
resolvedProjectReferences: state.program.getResolvedProjectReferences()?.map(toResolvedProjectReferenceOfProgramFromBuildInfo),
354358
resolvedTypeReferenceDirectives: state.program.getResolvedTypeReferenceDirectives(),
355359
automaticTypeDirectiveNames: state.program.getAutomaticTypeDirectiveNames(),
356360
fileProcessingDiagnostics: state.program.getFileProcessingDiagnostics(),
361+
useSourceOfProjectReferenceRedirect: state.program.useSourceOfProjectReferenceRedirect,
357362
};
358363

359364
function toSourceFileOfProgramFromBuildInfo(sourceFile: SourceFile): SourceFileOfProgramFromBuildInfo {
360365
if (state.program!.isSourceFileFromExternalLibraryPath(sourceFile.path)) (sourceFileFromExternalLibraryPath ||= new Set()).add(sourceFile.path);
366+
if (sourceFile.resolvedPath !== sourceFile.path && state.program!.isSourceFileFromExternalLibraryPath(sourceFile.path)) (sourceFileFromExternalLibraryPath ||= new Set()).add(sourceFile.path);
361367
const file: SourceFileOfProgramFromBuildInfo = {
362368
fileName: sourceFile.fileName,
363369
originalFileName: sourceFile.originalFileName,
@@ -878,6 +884,7 @@ namespace ts {
878884

879885
includeReasons: readonly PersistedProgramFileIncludeReason[];
880886
isSourceFileFromExternalLibraryPath?: true;
887+
isSourceFileFromProjectReference?: true;
881888
redirectTargets?: readonly ProgramBuildInfoAbsoluteFileId[];
882889
packageName?: string;
883890
}
@@ -924,6 +931,7 @@ namespace ts {
924931
resolvedTypeReferenceDirectives: readonly PersistedProgramResolutionEntry[] | undefined;
925932
fileProcessingDiagnostics: readonly PersistedProgramFilePreprocessingDiagnostic[] | undefined;
926933
resolutions: readonly PersistedProgramResolution[] | undefined;
934+
useSourceOfProjectReferenceRedirect: true | undefined;
927935
}
928936
export interface ProgramBuildInfo {
929937
fileNames: readonly string[];
@@ -1036,6 +1044,7 @@ namespace ts {
10361044
automaticTypeDirectiveNames: program.getAutomaticTypeDirectiveNames()?.length ? program.getAutomaticTypeDirectiveNames() : undefined,
10371045
fileProcessingDiagnostics: mapToReadonlyArrayOrUndefined(program.getFileProcessingDiagnostics(), toPersistedProgramFilePreprocessingDiagnostic),
10381046
resolutions: mapToReadonlyArrayOrUndefined(resolutions, toPersistedProgramResolution),
1047+
useSourceOfProjectReferenceRedirect: program.useSourceOfProjectReferenceRedirect ? true : undefined
10391048
};
10401049
}
10411050
return {
@@ -1109,6 +1118,7 @@ namespace ts {
11091118
redirectTargets: mapToReadonlyArrayOrUndefined(program.redirectTargetsMap.get(sourceFile.path), toAbsoluteFileId),
11101119
includeReasons: program.getFileIncludeReasons().get(sourceFile.path)!.map(toPersistedProgramFileIncludeReason),
11111120
isSourceFileFromExternalLibraryPath: program.isSourceFileFromExternalLibraryPath(sourceFile.path) ? true : undefined,
1121+
isSourceFileFromProjectReference: sourceFile.path !== sourceFile.resolvedPath && program.isSourceFileFromProjectReference(sourceFile) ? true : undefined,
11121122
packageName: program.sourceFileToPackageName.get(sourceFile.path),
11131123
};
11141124
}
@@ -1726,6 +1736,7 @@ namespace ts {
17261736
const filesByName = new Map<Path, SourceFileOfProgramFromBuildInfo | Path | typeof missingSourceOfProjectReferenceRedirect | typeof missingFile>();
17271737
const fileIncludeReasons = createMultiMap<Path, FileIncludeReason>();
17281738
let sourceFileFromExternalLibraryPath: Set<Path> | undefined;
1739+
let sourceFileFromProjectReferencePath: Set<Path> | undefined;
17291740
const redirectTargetsMap = createMultiMap<Path, string>();
17301741
const sourceFileToPackageName = new Map<Path, string>();
17311742
program.peristedProgram.filesByName?.forEach(entry => {
@@ -1745,13 +1756,15 @@ namespace ts {
17451756
filesByName,
17461757
fileIncludeReasons,
17471758
sourceFileFromExternalLibraryPath,
1759+
sourceFileFromProjectReferencePath,
17481760
redirectTargetsMap,
17491761
sourceFileToPackageName,
17501762
projectReferences: program.peristedProgram.projectReferences?.map(toProjectReference),
17511763
resolvedProjectReferences: program.peristedProgram.resolvedProjectReferences?.map(toResolvedProjectReference),
17521764
automaticTypeDirectiveNames: program.peristedProgram.automaticTypeDirectiveNames,
17531765
resolvedTypeReferenceDirectives: toResolutionMap(program.peristedProgram.resolvedTypeReferenceDirectives) || new Map(),
17541766
fileProcessingDiagnostics: map(program.peristedProgram.fileProcessingDiagnostics, toFileProcessingDiagnostic),
1767+
useSourceOfProjectReferenceRedirect: !!program.peristedProgram.useSourceOfProjectReferenceRedirect,
17551768
};
17561769
return {
17571770
program: createProgramFromPersistedProgramState(persistedProgramState, compilerOptions),
@@ -1764,6 +1777,7 @@ namespace ts {
17641777

17651778
fileIncludeReasons.set(path, file.includeReasons.map(toFileIncludeReason));
17661779
if (file.isSourceFileFromExternalLibraryPath) (sourceFileFromExternalLibraryPath ||= new Set()).add(path);
1780+
if (file.isSourceFileFromProjectReference) (sourceFileFromProjectReferencePath ||= new Set()).add(path);
17671781
if (file.redirectTargets) redirectTargetsMap.set(path, file.redirectTargets.map(toFileAbsolutePath));
17681782
if (file.packageName) sourceFileToPackageName.set(path, file.packageName);
17691783

@@ -1874,9 +1888,11 @@ namespace ts {
18741888
getResolvedTypeReferenceDirectives: () => persistedProgramState.resolvedTypeReferenceDirectives,
18751889
getFilesByNameMap: () => persistedProgramState.filesByName,
18761890
isSourceFileFromExternalLibraryPath: path => !!persistedProgramState.sourceFileFromExternalLibraryPath?.has(path),
1891+
isSourceFileFromProjectReference: file => !!persistedProgramState.sourceFileFromProjectReferencePath?.has(file.path),
18771892
getFileProcessingDiagnostics: () => persistedProgramState.fileProcessingDiagnostics,
18781893
redirectTargetsMap: persistedProgramState.redirectTargetsMap,
18791894
sourceFileToPackageName: persistedProgramState.sourceFileToPackageName,
1895+
useSourceOfProjectReferenceRedirect: persistedProgramState.useSourceOfProjectReferenceRedirect
18801896
};
18811897
}
18821898

src/compiler/program.ts

+14
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,8 @@ namespace ts {
11621162
realpath: host.realpath?.bind(host),
11631163
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
11641164
getFileIncludeReasons: () => fileReasons,
1165+
isSourceFileFromProjectReference,
1166+
useSourceOfProjectReferenceRedirect,
11651167
structureIsReused,
11661168
};
11671169

@@ -1569,6 +1571,11 @@ namespace ts {
15691571
newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
15701572
newSourceFile.fileName = oldSourceFile.fileName;
15711573

1574+
if (oldProgram.useSourceOfProjectReferenceRedirect !== useSourceOfProjectReferenceRedirect &&
1575+
(newSourceFile.path !== newSourceFile.resolvedPath || oldProgram.isSourceFileFromProjectReference(oldSourceFile as SourceFile & SourceFileOfProgramFromBuildInfo))) {
1576+
return StructureIsReused.Not;
1577+
}
1578+
15721579
const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
15731580
if (packageName !== undefined) {
15741581
// If there are 2 different source files for the same package name and at least one of them changes,
@@ -2849,6 +2856,13 @@ namespace ts {
28492856
return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
28502857
}
28512858

2859+
function isSourceFileFromProjectReference(file: SourceFile) {
2860+
return file.resolvedPath !== file.path ||
2861+
useSourceOfProjectReferenceRedirect ?
2862+
!!getProjectReferenceRedirectProject(file.fileName) : // Is this source of project reference
2863+
!!getSourceOfProjectReferenceRedirect(file.fileName); // Is this output of project reference
2864+
}
2865+
28522866
function forEachResolvedProjectReference<T>(
28532867
cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
28542868
): T | undefined {

src/compiler/types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -3956,6 +3956,8 @@ namespace ts {
39563956
/** Is the file emitted file */
39573957
/* @internal */ isEmittedFile(file: string): boolean;
39583958
/* @internal */ getFileIncludeReasons(): MultiMap<Path, FileIncludeReason>;
3959+
/* @internal */ useSourceOfProjectReferenceRedirect: boolean;
3960+
/* @internal */ isSourceFileFromProjectReference(file: SourceFile): boolean;
39593961
/* @internal */ useCaseSensitiveFileNames(): boolean;
39603962

39613963
getProjectReferences(): readonly ProjectReference[] | undefined;
@@ -4045,7 +4047,9 @@ namespace ts {
40454047
getFilesByNameMap(): ESMap<Path, SourceFileOfProgramFromBuildInfo | Path | typeof missingSourceOfProjectReferenceRedirect | typeof missingFile>;
40464048
isSourceFileFromExternalLibraryPath(path: Path): boolean;
40474049
getFileProcessingDiagnostics(): FilePreprocessingDiagnostic[] | undefined;
4050+
isSourceFileFromProjectReference(file: SourceFileOfProgramFromBuildInfo): boolean;
40484051

4052+
useSourceOfProjectReferenceRedirect: boolean;
40494053
redirectTargetsMap: MultiMap<Path, string>;
40504054
sourceFileToPackageName: ESMap<Path, string>;
40514055
}

tests/baselines/reference/tsserver/persistResolutions/uses-saved-resolution-for-program-with-project-where-dts-file-contains-fewer-modules-than-original-file.js

+46-27
Original file line numberDiff line numberDiff line change
@@ -62,41 +62,56 @@ DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core 1 u
6262
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core 1 undefined Config: /user/username/projects/myproject/core/tsconfig.json WatchType: Wild card directory
6363
FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info
6464
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/myClass.d.ts 500 undefined WatchType: Closed Script info
65-
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/anotherClass.d.ts 500 undefined WatchType: Closed Script info
66-
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/logic/index.d.ts 500 undefined WatchType: Closed Script info
65+
Reusing resolution of module '../logic' from '/user/username/projects/myproject/tests/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/logic/index.ts'.
66+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/logic/index.ts 500 undefined WatchType: Closed Script info
67+
Reusing resolution of module '../core/myClass' from '/user/username/projects/myproject/logic/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/core/myClass.ts'.
68+
Reusing resolution of module '../core/anotherClass' from '/user/username/projects/myproject/logic/index.ts' of old program, it was successfully resolved to '/user/username/projects/myproject/core/anotherClass.ts'.
69+
======== Resolving module '../core' from '/user/username/projects/myproject/logic/index.ts'. ========
70+
Using compiler options of project reference redirect '/user/username/projects/myproject/logic/tsconfig.json'.
71+
Module resolution kind is not specified, using 'NodeJs'.
72+
Loading module as file / folder, candidate module location '/user/username/projects/myproject/core', target file type 'TypeScript'.
73+
File '/user/username/projects/myproject/core.ts' does not exist.
74+
File '/user/username/projects/myproject/core.tsx' does not exist.
75+
File '/user/username/projects/myproject/core.d.ts' does not exist.
76+
File '/user/username/projects/myproject/core/package.json' does not exist.
77+
File '/user/username/projects/myproject/core/index.ts' exist - use it as a name resolution result.
78+
======== Module name '../core' was successfully resolved to '/user/username/projects/myproject/core/index.ts'. ========
79+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/myClass.ts 500 undefined WatchType: Closed Script info
80+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/index.ts 500 undefined WatchType: Closed Script info
81+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/anotherClass.ts 500 undefined WatchType: Closed Script info
6782
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tests/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tests/tsconfig.json WatchType: Type roots
6883
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tests/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tests/tsconfig.json WatchType: Type roots
6984
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tests/tsconfig.json WatchType: Type roots
7085
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tests/tsconfig.json WatchType: Type roots
71-
Finishing updateGraphWorker: Project: /user/username/projects/myproject/tests/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Completely Elapsed:: *ms
86+
Finishing updateGraphWorker: Project: /user/username/projects/myproject/tests/tsconfig.json Version: 1 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms
7287
Project '/user/username/projects/myproject/tests/tsconfig.json' (Configured)
73-
Files (5)
88+
Files (6)
7489
/a/lib/lib.d.ts
75-
/user/username/projects/myproject/core/myClass.d.ts
76-
/user/username/projects/myproject/core/anotherClass.d.ts
77-
/user/username/projects/myproject/logic/index.d.ts
90+
/user/username/projects/myproject/core/myClass.ts
91+
/user/username/projects/myproject/core/index.ts
92+
/user/username/projects/myproject/core/anotherClass.ts
93+
/user/username/projects/myproject/logic/index.ts
7894
/user/username/projects/myproject/tests/index.ts
7995

8096

8197
../../../../../a/lib/lib.d.ts
8298
Default library
83-
../core/myClass.d.ts
84-
Imported via "../core/myClass" from file '../logic/index.d.ts'
85-
File is output of project reference source '../core/myClass.ts'
86-
../core/anotherClass.d.ts
87-
Imported via "../core/anotherClass" from file '../logic/index.d.ts'
88-
File is output of project reference source '../core/anotherClass.ts'
89-
../logic/index.d.ts
99+
../core/myClass.ts
100+
Imported via "../core/myClass" from file '../logic/index.ts'
101+
../core/index.ts
102+
Imported via "../core" from file '../logic/index.ts'
103+
../core/anotherClass.ts
104+
Imported via "../core/anotherClass" from file '../logic/index.ts'
105+
../logic/index.ts
90106
Imported via "../logic" from file 'index.ts'
91-
File is output of project reference source '../logic/index.ts'
92107
index.ts
93108
Matched by include pattern '**/*' in 'tsconfig.json'
94109

95110
-----------------------------------------------
96111
Search path: /user/username/projects/myproject/tests
97112
For info: /user/username/projects/myproject/tests/tsconfig.json :: No config files found.
98113
Project '/user/username/projects/myproject/tests/tsconfig.json' (Configured)
99-
Files (5)
114+
Files (6)
100115

101116
-----------------------------------------------
102117
Open files:
@@ -117,22 +132,26 @@ interface RegExp {}
117132
interface String { charAt: any; }
118133
interface Array<T> { length: number; [n: number]: T; }
119134

120-
{"fileName":"/user/username/projects/myproject/core/myClass.d.ts","version":"-7432826827-export declare class myClass {\n}\n"}
121-
export declare class myClass {
122-
}
123-
135+
{"fileName":"/user/username/projects/myproject/core/myClass.ts","version":"-11785903855-export class myClass { }"}
136+
export class myClass { }
124137

125-
{"fileName":"/user/username/projects/myproject/core/anotherClass.d.ts","version":"-6928009824-export declare class anotherClass {\n}\n"}
126-
export declare class anotherClass {
127-
}
138+
{"fileName":"/user/username/projects/myproject/core/index.ts","version":"4120767815-export function bar() { return 10; }"}
139+
export function bar() { return 10; }
128140

141+
{"fileName":"/user/username/projects/myproject/core/anotherClass.ts","version":"-6664885476-export class anotherClass { }"}
142+
export class anotherClass { }
129143

130-
{"fileName":"/user/username/projects/myproject/logic/index.d.ts","version":"-26318514585-import { myClass } from \"../core/myClass\";\nimport { anotherClass } from \"../core/anotherClass\";\nexport declare function returnMyClass(): myClass;\nexport declare function returnAnotherClass(): anotherClass;\n"}
144+
{"fileName":"/user/username/projects/myproject/logic/index.ts","version":"-8233748805-import { myClass } from \"../core/myClass\";\nimport { bar } from \"../core\";\nimport { anotherClass } from \"../core/anotherClass\";\nexport function returnMyClass() {\n bar();\n return new myClass();\n}\nexport function returnAnotherClass() {\n return new anotherClass();\n}"}
131145
import { myClass } from "../core/myClass";
146+
import { bar } from "../core";
132147
import { anotherClass } from "../core/anotherClass";
133-
export declare function returnMyClass(): myClass;
134-
export declare function returnAnotherClass(): anotherClass;
135-
148+
export function returnMyClass() {
149+
bar();
150+
return new myClass();
151+
}
152+
export function returnAnotherClass() {
153+
return new anotherClass();
154+
}
136155

137156
{"fileName":"/user/username/projects/myproject/tests/index.ts","version":"-2125404654-import { returnMyClass } from \"../logic\";\nreturnMyClass();"}
138157
import { returnMyClass } from "../logic";

0 commit comments

Comments
 (0)