Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d84431e

Browse files
authored
Handle root files listed in project config from referenced project to be same as if they were included through import (microsoft#58560)
1 parent 79a8514 commit d84431e

17 files changed

+6870
-91
lines changed

src/compiler/builder.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
addRange,
33
AffectedFileResult,
4+
append,
45
arrayFrom,
56
arrayToMap,
67
BuilderProgram,
@@ -990,10 +991,13 @@ export type ProgramBuildInfoRootStartEnd = [start: ProgramBuildInfoFileId, end:
990991
*/
991992
export type ProgramBuildInfoRoot = ProgramBuildInfoRootStartEnd | ProgramBuildInfoFileId;
992993
/** @internal */
994+
export type ProgramBuildInfoResolvedRoot = [resolved: ProgramBuildInfoFileId, root: ProgramBuildInfoFileId];
995+
/** @internal */
993996
export interface ProgramMultiFileEmitBuildInfo {
994997
fileNames: readonly string[];
995998
fileInfos: readonly ProgramMultiFileEmitBuildInfoFileInfo[];
996999
root: readonly ProgramBuildInfoRoot[];
1000+
resolvedRoot: readonly ProgramBuildInfoResolvedRoot[] | undefined;
9971001
options: CompilerOptions | undefined;
9981002
fileIdsList: readonly (readonly ProgramBuildInfoFileId[])[] | undefined;
9991003
referencedMap: ProgramBuildInfoReferencedMap | undefined;
@@ -1023,6 +1027,7 @@ export interface ProgramBundleEmitBuildInfo {
10231027
fileNames: readonly string[];
10241028
fileInfos: readonly ProgramBundleEmitBuildInfoFileInfo[];
10251029
root: readonly ProgramBuildInfoRoot[];
1030+
resolvedRoot: readonly ProgramBuildInfoResolvedRoot[] | undefined;
10261031
options: CompilerOptions | undefined;
10271032
outSignature: EmitSignature | undefined;
10281033
latestChangedDtsFile: string | undefined;
@@ -1047,6 +1052,7 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
10471052
const latestChangedDtsFile = state.latestChangedDtsFile ? relativeToBuildInfoEnsuringAbsolutePath(state.latestChangedDtsFile) : undefined;
10481053
const fileNames: string[] = [];
10491054
const fileNameToFileId = new Map<string, ProgramBuildInfoFileId>();
1055+
const rootFileNames = new Set(state.program!.getRootFileNames().map(f => toPath(f, currentDirectory, state.program!.getCanonicalFileName)));
10501056
const root: ProgramBuildInfoRoot[] = [];
10511057
if (state.compilerOptions.outFile) {
10521058
// Copy all fileInfo, version and impliedFormat
@@ -1063,6 +1069,7 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
10631069
fileNames,
10641070
fileInfos,
10651071
root,
1072+
resolvedRoot: toResolvedRoot(),
10661073
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
10671074
outSignature: state.outSignature,
10681075
latestChangedDtsFile,
@@ -1090,7 +1097,8 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
10901097
if (!isJsonSourceFile(file) && sourceFileMayBeEmitted(file, state.program!)) {
10911098
const emitSignature = state.emitSignatures?.get(key);
10921099
if (emitSignature !== actualSignature) {
1093-
(emitSignatures ||= []).push(
1100+
emitSignatures = append(
1101+
emitSignatures,
10941102
emitSignature === undefined ?
10951103
fileId : // There is no emit, encode as false
10961104
// fileId, signature: emptyArray if signature only differs in dtsMap option than our own compilerOptions otherwise EmitSignature
@@ -1133,7 +1141,8 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
11331141
const file = state.program!.getSourceFileByPath(path);
11341142
if (!file || !sourceFileMayBeEmitted(file, state.program!)) continue;
11351143
const fileId = toFileId(path), pendingEmit = state.affectedFilesPendingEmit.get(path)!;
1136-
(affectedFilesPendingEmit ||= []).push(
1144+
affectedFilesPendingEmit = append(
1145+
affectedFilesPendingEmit,
11371146
pendingEmit === fullEmitForOptions ?
11381147
fileId : // Pending full emit per options
11391148
pendingEmit === BuilderFileEmit.Dts ?
@@ -1147,14 +1156,15 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
11471156
let changeFileSet: ProgramBuildInfoFileId[] | undefined;
11481157
if (state.changedFilesSet.size) {
11491158
for (const path of arrayFrom(state.changedFilesSet.keys()).sort(compareStringsCaseSensitive)) {
1150-
(changeFileSet ||= []).push(toFileId(path));
1159+
changeFileSet = append(changeFileSet, toFileId(path));
11511160
}
11521161
}
11531162
const emitDiagnosticsPerFile = convertToProgramBuildInfoDiagnostics(state.emitDiagnosticsPerFile);
11541163
const program: ProgramMultiFileEmitBuildInfo = {
11551164
fileNames,
11561165
fileInfos,
11571166
root,
1167+
resolvedRoot: toResolvedRoot(),
11581168
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
11591169
fileIdsList,
11601170
referencedMap,
@@ -1189,8 +1199,8 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
11891199
const key = fileIds.join();
11901200
let fileIdListId = fileNamesToFileIdListId?.get(key);
11911201
if (fileIdListId === undefined) {
1192-
(fileIdsList ||= []).push(fileIds);
1193-
(fileNamesToFileIdListId ||= new Map()).set(key, fileIdListId = fileIdsList.length as ProgramBuildInfoFileIdListId);
1202+
fileIdsList = append(fileIdsList, fileIds);
1203+
(fileNamesToFileIdListId ??= new Map()).set(key, fileIdListId = fileIdsList.length as ProgramBuildInfoFileIdListId);
11941204
}
11951205
return fileIdListId;
11961206
}
@@ -1214,6 +1224,17 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
12141224
return root.length = root.length - 1;
12151225
}
12161226

1227+
function toResolvedRoot(): ProgramBuildInfoResolvedRoot[] | undefined {
1228+
let result: ProgramBuildInfoResolvedRoot[] | undefined;
1229+
rootFileNames.forEach(path => {
1230+
const file = state.program!.getSourceFileByPath(path);
1231+
if (file && path !== file.resolvedPath) {
1232+
result = append(result, [toFileId(file.resolvedPath), toFileId(path)]);
1233+
}
1234+
});
1235+
return result;
1236+
}
1237+
12171238
/**
12181239
* @param optionKey key of CommandLineOption to use to determine if the option should be serialized in tsbuildinfo
12191240
*/
@@ -1253,7 +1274,8 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
12531274
if (diagnostics) {
12541275
for (const key of arrayFrom(diagnostics.keys()).sort(compareStringsCaseSensitive)) {
12551276
const value = diagnostics.get(key)!;
1256-
(result ||= []).push(
1277+
result = append(
1278+
result,
12571279
value.length ?
12581280
[
12591281
toFileId(key),
@@ -1909,7 +1931,9 @@ export function getBuildInfoFileVersionMap(
19091931
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
19101932
const fileInfos = new Map<Path, string>();
19111933
let rootIndex = 0;
1912-
const roots: Path[] = [];
1934+
// Root name to resolved
1935+
const roots = new Map<Path, Path | undefined>();
1936+
const resolvedRoots = new Map(program.resolvedRoot);
19131937
program.fileInfos.forEach((fileInfo, index) => {
19141938
const path = toPath(program.fileNames[index], buildInfoDirectory, getCanonicalFileName);
19151939
const version = isString(fileInfo) ? fileInfo : fileInfo.version;
@@ -1919,17 +1943,27 @@ export function getBuildInfoFileVersionMap(
19191943
const fileId = (index + 1) as ProgramBuildInfoFileId;
19201944
if (isArray(current)) {
19211945
if (current[0] <= fileId && fileId <= current[1]) {
1922-
roots.push(path);
1946+
addRoot(fileId, path);
19231947
if (current[1] === fileId) rootIndex++;
19241948
}
19251949
}
19261950
else if (current === fileId) {
1927-
roots.push(path);
1951+
addRoot(fileId, path);
19281952
rootIndex++;
19291953
}
19301954
}
19311955
});
19321956
return { fileInfos, roots };
1957+
1958+
function addRoot(fileId: ProgramBuildInfoFileId, path: Path) {
1959+
const root = resolvedRoots.get(fileId);
1960+
if (root) {
1961+
roots.set(toPath(program.fileNames[root - 1], buildInfoDirectory, getCanonicalFileName), path);
1962+
}
1963+
else {
1964+
roots.set(path, undefined);
1965+
}
1966+
}
19331967
}
19341968

19351969
/** @internal */

src/compiler/program.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3765,7 +3765,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
37653765
}
37663766

37673767
let redirectedPath: Path | undefined;
3768-
if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) {
3768+
if (!useSourceOfProjectReferenceRedirect) {
37693769
const redirectProject = getProjectReferenceRedirectProject(fileName);
37703770
if (redirectProject) {
37713771
if (redirectProject.commandLine.options.outFile) {

src/compiler/tsbuildPublic.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
findIndex,
4949
flattenDiagnosticMessageText,
5050
forEach,
51+
forEachEntry,
5152
forEachKey,
5253
ForegroundColorEscapeSequences,
5354
formatColorAndReset,
@@ -1734,15 +1735,17 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
17341735
};
17351736
}
17361737

1738+
const inputPath = buildInfoProgram ? toPath(state, inputFile) : undefined;
17371739
// If an buildInfo is older than the newest input, we can stop checking
17381740
if (buildInfoTime && buildInfoTime < inputTime) {
17391741
let version: string | undefined;
17401742
let currentVersion: string | undefined;
17411743
if (buildInfoProgram) {
17421744
// Read files and see if they are same, read is anyways cached
17431745
if (!buildInfoVersionMap) buildInfoVersionMap = getBuildInfoFileVersionMap(buildInfoProgram, buildInfoPath!, host);
1744-
version = buildInfoVersionMap.fileInfos.get(toPath(state, inputFile));
1745-
const text = version ? state.readFileWithCache(inputFile) : undefined;
1746+
const resolvedInputPath = buildInfoVersionMap.roots.get(inputPath!);
1747+
version = buildInfoVersionMap.fileInfos.get(resolvedInputPath ?? inputPath!);
1748+
const text = version ? state.readFileWithCache(resolvedInputPath ?? inputFile) : undefined;
17461749
currentVersion = text !== undefined ? getSourceFileVersionAsHashFromText(host, text) : undefined;
17471750
if (version && version === currentVersion) pseudoInputUpToDate = true;
17481751
}
@@ -1761,20 +1764,22 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
17611764
newestInputFileTime = inputTime;
17621765
}
17631766

1764-
if (buildInfoProgram) seenRoots.add(toPath(state, inputFile));
1767+
if (buildInfoProgram) seenRoots.add(inputPath!);
17651768
}
17661769

17671770
if (buildInfoProgram) {
17681771
if (!buildInfoVersionMap) buildInfoVersionMap = getBuildInfoFileVersionMap(buildInfoProgram, buildInfoPath!, host);
1769-
for (const existingRoot of buildInfoVersionMap.roots) {
1770-
if (!seenRoots.has(existingRoot)) {
1771-
// File was root file when project was built but its not any more
1772-
return {
1773-
type: UpToDateStatusType.OutOfDateRoots,
1774-
buildInfoFile: buildInfoPath!,
1775-
inputFile: existingRoot,
1776-
};
1777-
}
1772+
const existingRoot = forEachEntry(
1773+
buildInfoVersionMap.roots,
1774+
// File was root file when project was built but its not any more
1775+
(_resolved, existingRoot) => !seenRoots.has(existingRoot) ? existingRoot : undefined,
1776+
);
1777+
if (existingRoot) {
1778+
return {
1779+
type: UpToDateStatusType.OutOfDateRoots,
1780+
buildInfoFile: buildInfoPath!,
1781+
inputFile: existingRoot,
1782+
};
17781783
}
17791784
}
17801785

src/testRunner/tests.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export * from "./unittests/tsbuildWatch/programUpdates.js";
110110
export * from "./unittests/tsbuildWatch/projectsBuilding.js";
111111
export * from "./unittests/tsbuildWatch/publicApi.js";
112112
export * from "./unittests/tsbuildWatch/reexport.js";
113+
export * from "./unittests/tsbuildWatch/roots.js";
113114
export * from "./unittests/tsbuildWatch/watchEnvironment.js";
114115
export * from "./unittests/tsc/cancellationToken.js";
115116
export * from "./unittests/tsc/composite.js";
@@ -197,6 +198,7 @@ export * from "./unittests/tsserver/projectReferenceCompileOnSave.js";
197198
export * from "./unittests/tsserver/projectReferenceErrors.js";
198199
export * from "./unittests/tsserver/projectReferences.js";
199200
export * from "./unittests/tsserver/projectReferencesSourcemap.js";
201+
export * from "./unittests/tsserver/projectRootFiles.js";
200202
export * from "./unittests/tsserver/projects.js";
201203
export * from "./unittests/tsserver/projectsWithReferences.js";
202204
export * from "./unittests/tsserver/refactors.js";

src/testRunner/unittests/helpers/baseline.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,16 @@ export type ReadableProgramBuildInfoFileInfo<T> = Omit<ts.BuilderState.FileInfo,
141141
export type ReadableProgramBuildInfoRoot =
142142
| [original: ts.ProgramBuildInfoFileId, readable: string]
143143
| [original: ts.ProgramBuildInfoRootStartEnd, readable: readonly string[]];
144-
export type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmitBuildInfo, "fileIdsList" | "fileInfos" | "root" | "referencedMap" | "semanticDiagnosticsPerFile" | "emitDiagnosticsPerFile" | "affectedFilesPendingEmit" | "changeFileSet" | "emitSignatures"> & {
144+
145+
export type ReadableProgramBuildInfoResolvedRoot = [
146+
original: ts.ProgramBuildInfoResolvedRoot,
147+
readable: [resolved: string, root: string],
148+
];
149+
export type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmitBuildInfo, "fileIdsList" | "fileInfos" | "root" | "resolvedRoot" | "referencedMap" | "semanticDiagnosticsPerFile" | "emitDiagnosticsPerFile" | "affectedFilesPendingEmit" | "changeFileSet" | "emitSignatures"> & {
145150
fileNamesList: readonly (readonly string[])[] | undefined;
146151
fileInfos: ts.MapLike<ReadableProgramBuildInfoFileInfo<ts.ProgramMultiFileEmitBuildInfoFileInfo>>;
147152
root: readonly ReadableProgramBuildInfoRoot[];
153+
resolvedRoot: readonly ReadableProgramBuildInfoResolvedRoot[] | undefined;
148154
referencedMap: ts.MapLike<string[]> | undefined;
149155
semanticDiagnosticsPerFile: readonly ReadableProgramBuildInfoDiagnostic[] | undefined;
150156
emitDiagnosticsPerFile: readonly ReadableProgramBuildInfoDiagnostic[] | undefined;
@@ -153,9 +159,10 @@ export type ReadableProgramMultiFileEmitBuildInfo = Omit<ts.ProgramMultiFileEmit
153159
emitSignatures: readonly ReadableProgramBuildInfoEmitSignature[] | undefined;
154160
};
155161
export type ReadableProgramBuildInfoBundlePendingEmit = [emitKind: ReadableBuilderFileEmit, original: ts.ProgramBuildInfoBundlePendingEmit];
156-
export type ReadableProgramBundleEmitBuildInfo = Omit<ts.ProgramBundleEmitBuildInfo, "fileInfos" | "root" | "pendingEmit"> & {
162+
export type ReadableProgramBundleEmitBuildInfo = Omit<ts.ProgramBundleEmitBuildInfo, "fileInfos" | "root" | "resolvedRoot" | "pendingEmit"> & {
157163
fileInfos: ts.MapLike<string | ReadableProgramBuildInfoFileInfo<ts.BuilderState.FileInfo>>;
158164
root: readonly ReadableProgramBuildInfoRoot[];
165+
resolvedRoot: readonly ReadableProgramBuildInfoResolvedRoot[] | undefined;
159166
pendingEmit: ReadableProgramBuildInfoBundlePendingEmit | undefined;
160167
};
161168

@@ -180,6 +187,7 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
180187
...buildInfo.program,
181188
fileInfos,
182189
root: buildInfo.program.root.map(toReadableProgramBuildInfoRoot),
190+
resolvedRoot: buildInfo.program.resolvedRoot?.map(toReadableProgramBuildInfoResolvedRoot),
183191
pendingEmit: pendingEmit === undefined ?
184192
undefined :
185193
[
@@ -198,6 +206,7 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
198206
fileNamesList,
199207
fileInfos: buildInfo.program.fileInfos ? fileInfos : undefined!,
200208
root: buildInfo.program.root.map(toReadableProgramBuildInfoRoot),
209+
resolvedRoot: buildInfo.program.resolvedRoot?.map(toReadableProgramBuildInfoResolvedRoot),
201210
options: buildInfo.program.options,
202211
referencedMap: toMapOfReferencedSet(buildInfo.program.referencedMap),
203212
semanticDiagnosticsPerFile: toReadableProgramBuildInfoDiagnosticsPerFile(buildInfo.program.semanticDiagnosticsPerFile),
@@ -245,6 +254,10 @@ function generateBuildInfoProgramBaseline(sys: ts.System, buildInfoPath: string,
245254
return [original, readable];
246255
}
247256

257+
function toReadableProgramBuildInfoResolvedRoot(original: ts.ProgramBuildInfoResolvedRoot): ReadableProgramBuildInfoResolvedRoot {
258+
return [original, [toFileName(original[0]), toFileName(original[1])]];
259+
}
260+
248261
function toMapOfReferencedSet(referenceMap: ts.ProgramBuildInfoReferencedMap | undefined): ts.MapLike<string[]> | undefined {
249262
if (!referenceMap) return undefined;
250263
const result: ts.MapLike<string[]> = {};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { dedent } from "../../_namespaces/Utils.js";
2+
import { jsonToReadableText } from "../helpers.js";
3+
import {
4+
FsContents,
5+
libContent,
6+
} from "./contents.js";
7+
import { libFile } from "./virtualFileSystemWithWatch.js";
8+
9+
function getFsContentsForRootsFromReferencedProject(serverFirst: boolean): FsContents {
10+
return {
11+
"/home/src/workspaces/tsconfig.json": jsonToReadableText({
12+
compilerOptions: {
13+
composite: true,
14+
},
15+
references: [
16+
{ path: "projects/server" },
17+
{ path: "projects/shared" },
18+
],
19+
}),
20+
"/home/src/workspaces/projects/shared/src/myClass.ts": `export class MyClass { }`,
21+
"/home/src/workspaces/projects/shared/src/logging.ts": dedent`
22+
export function log(str: string) {
23+
console.log(str);
24+
}
25+
`,
26+
"/home/src/workspaces/projects/shared/src/random.ts": dedent`
27+
export function randomFn(str: string) {
28+
console.log(str);
29+
}
30+
`,
31+
"/home/src/workspaces/projects/shared/tsconfig.json": jsonToReadableText({
32+
extends: "../../tsconfig.json",
33+
compilerOptions: {
34+
outDir: "./dist",
35+
},
36+
include: ["src/**/*.ts"],
37+
}),
38+
"/home/src/workspaces/projects/server/src/server.ts": dedent`
39+
import { MyClass } from ':shared/myClass.js';
40+
console.log('Hello, world!');
41+
`,
42+
"/home/src/workspaces/projects/server/tsconfig.json": jsonToReadableText({
43+
extends: "../../tsconfig.json",
44+
compilerOptions: {
45+
baseUrl: "./src",
46+
rootDir: "..",
47+
outDir: "./dist",
48+
paths: {
49+
":shared/*": ["../../shared/src/*"],
50+
},
51+
},
52+
include: serverFirst ?
53+
["src/**/*.ts", "../shared/src/**/*.ts"] :
54+
["../shared/src/**/*.ts", "src/**/*.ts"],
55+
references: [
56+
{ path: "../shared" },
57+
],
58+
}),
59+
[libFile.path]: libContent,
60+
};
61+
}
62+
63+
export function forEachScenarioForRootsFromReferencedProject(action: (subScenario: string, getFsContents: () => FsContents) => void) {
64+
action("when root file is from referenced project", () => getFsContentsForRootsFromReferencedProject(/*serverFirst*/ true));
65+
action("when root file is from referenced project and shared is first", () => getFsContentsForRootsFromReferencedProject(/*serverFirst*/ false));
66+
}

0 commit comments

Comments
 (0)