Skip to content

Commit f0db440

Browse files
committed
Handle the difference is useSourceOfProjectReferenceRedirect and project reference files in the program so editor can use correct program
1 parent 5d658c6 commit f0db440

5 files changed

+117
-52
lines changed

src/compiler/builder.ts

Lines changed: 16 additions & 0 deletions
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

Lines changed: 14 additions & 0 deletions
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

@@ -1564,6 +1566,11 @@ namespace ts {
15641566
newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
15651567
newSourceFile.fileName = oldSourceFile.fileName;
15661568

1569+
if (oldProgram.useSourceOfProjectReferenceRedirect !== useSourceOfProjectReferenceRedirect &&
1570+
(newSourceFile.path !== newSourceFile.resolvedPath || oldProgram.isSourceFileFromProjectReference(oldSourceFile as SourceFile & SourceFileOfProgramFromBuildInfo))) {
1571+
return StructureIsReused.Not;
1572+
}
1573+
15671574
const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
15681575
if (packageName !== undefined) {
15691576
// If there are 2 different source files for the same package name and at least one of them changes,
@@ -2844,6 +2851,13 @@ namespace ts {
28442851
return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
28452852
}
28462853

2854+
function isSourceFileFromProjectReference(file: SourceFile) {
2855+
return file.resolvedPath !== file.path ||
2856+
useSourceOfProjectReferenceRedirect ?
2857+
!!getProjectReferenceRedirectProject(file.fileName) : // Is this source of project reference
2858+
!!getSourceOfProjectReferenceRedirect(file.fileName); // Is this output of project reference
2859+
}
2860+
28472861
function forEachResolvedProjectReference<T>(
28482862
cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
28492863
): T | undefined {

src/compiler/types.ts

Lines changed: 4 additions & 0 deletions
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(): ReadonlyESMap<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

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,41 +63,56 @@ DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core 1 u
6363
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core 1 undefined Config: /user/username/projects/myproject/core/tsconfig.json WatchType: Wild card directory
6464
FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info
6565
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/myClass.d.ts 500 undefined WatchType: Closed Script info
66-
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/anotherClass.d.ts 500 undefined WatchType: Closed Script info
67-
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/logic/index.d.ts 500 undefined WatchType: Closed Script info
66+
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'.
67+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/logic/index.ts 500 undefined WatchType: Closed Script info
68+
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'.
69+
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'.
70+
======== Resolving module '../core' from '/user/username/projects/myproject/logic/index.ts'. ========
71+
Using compiler options of project reference redirect '/user/username/projects/myproject/logic/tsconfig.json'.
72+
Module resolution kind is not specified, using 'NodeJs'.
73+
Loading module as file / folder, candidate module location '/user/username/projects/myproject/core', target file type 'TypeScript'.
74+
File '/user/username/projects/myproject/core.ts' does not exist.
75+
File '/user/username/projects/myproject/core.tsx' does not exist.
76+
File '/user/username/projects/myproject/core.d.ts' does not exist.
77+
File '/user/username/projects/myproject/core/package.json' does not exist.
78+
File '/user/username/projects/myproject/core/index.ts' exist - use it as a name resolution result.
79+
======== Module name '../core' was successfully resolved to '/user/username/projects/myproject/core/index.ts'. ========
80+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/myClass.ts 500 undefined WatchType: Closed Script info
81+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/index.ts 500 undefined WatchType: Closed Script info
82+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/core/anotherClass.ts 500 undefined WatchType: Closed Script info
6883
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
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
7085
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tests/tsconfig.json WatchType: Type roots
7186
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
72-
Finishing updateGraphWorker: Project: /user/username/projects/myproject/tests/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Completely Elapsed:: *ms
87+
Finishing updateGraphWorker: Project: /user/username/projects/myproject/tests/tsconfig.json Version: 1 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms
7388
Project '/user/username/projects/myproject/tests/tsconfig.json' (Configured)
74-
Files (5)
89+
Files (6)
7590
/a/lib/lib.d.ts
76-
/user/username/projects/myproject/core/myClass.d.ts
77-
/user/username/projects/myproject/core/anotherClass.d.ts
78-
/user/username/projects/myproject/logic/index.d.ts
91+
/user/username/projects/myproject/core/myClass.ts
92+
/user/username/projects/myproject/core/index.ts
93+
/user/username/projects/myproject/core/anotherClass.ts
94+
/user/username/projects/myproject/logic/index.ts
7995
/user/username/projects/myproject/tests/index.ts
8096

8197

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

96111
-----------------------------------------------
97112
Search path: /user/username/projects/myproject/tests
98113
For info: /user/username/projects/myproject/tests/tsconfig.json :: No config files found.
99114
Project '/user/username/projects/myproject/tests/tsconfig.json' (Configured)
100-
Files (5)
115+
Files (6)
101116

102117
-----------------------------------------------
103118
Open files:
@@ -119,22 +134,26 @@ interface RegExp {}
119134
interface String { charAt: any; }
120135
interface Array<T> { length: number; [n: number]: T; }
121136

122-
{"fileName":"/user/username/projects/myproject/core/myClass.d.ts","version":"-7432826827-export declare class myClass {\n}\n"}
123-
export declare class myClass {
124-
}
125-
137+
{"fileName":"/user/username/projects/myproject/core/myClass.ts","version":"-11785903855-export class myClass { }"}
138+
export class myClass { }
126139

127-
{"fileName":"/user/username/projects/myproject/core/anotherClass.d.ts","version":"-6928009824-export declare class anotherClass {\n}\n"}
128-
export declare class anotherClass {
129-
}
140+
{"fileName":"/user/username/projects/myproject/core/index.ts","version":"4120767815-export function bar() { return 10; }"}
141+
export function bar() { return 10; }
130142

143+
{"fileName":"/user/username/projects/myproject/core/anotherClass.ts","version":"-6664885476-export class anotherClass { }"}
144+
export class anotherClass { }
131145

132-
{"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"}
146+
{"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}"}
133147
import { myClass } from "../core/myClass";
148+
import { bar } from "../core";
134149
import { anotherClass } from "../core/anotherClass";
135-
export declare function returnMyClass(): myClass;
136-
export declare function returnAnotherClass(): anotherClass;
137-
150+
export function returnMyClass() {
151+
bar();
152+
return new myClass();
153+
}
154+
export function returnAnotherClass() {
155+
return new anotherClass();
156+
}
138157

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

0 commit comments

Comments
 (0)