Skip to content

Commit c3e132d

Browse files
authored
Keep extended config's raw file, include, exclude relative to itself and correct it when setting extending options (#42544)
* Test when config file extends is incorrectly computed Test for #40720 * Keep extended config's raw file, include, exclude relative to itself and correct it when setting extending options Fixes #40720
1 parent 1c25b00 commit c3e132d

File tree

5 files changed

+382
-23
lines changed

5 files changed

+382
-23
lines changed

src/compiler/commandLineParser.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,14 +2617,17 @@ namespace ts {
26172617
if (ownConfig.extendedConfigPath) {
26182618
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
26192619
resolutionStack = resolutionStack.concat([resolvedPath]);
2620-
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, basePath, resolutionStack, errors, extendedConfigCache);
2620+
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, resolutionStack, errors, extendedConfigCache);
26212621
if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) {
26222622
const baseRaw = extendedConfig.raw;
26232623
const raw = ownConfig.raw;
2624+
let relativeDifference: string | undefined ;
26242625
const setPropertyInRawIfNotUndefined = (propertyName: string) => {
2625-
const value = raw[propertyName] || baseRaw[propertyName];
2626-
if (value) {
2627-
raw[propertyName] = value;
2626+
if (!raw[propertyName] && baseRaw[propertyName]) {
2627+
raw[propertyName] = map(baseRaw[propertyName], (path: string) => isRootedDiskPath(path) ? path : combinePaths(
2628+
relativeDifference ||= convertToRelativePath(getDirectoryPath(ownConfig.extendedConfigPath!), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)),
2629+
path
2630+
));
26282631
}
26292632
};
26302633
setPropertyInRawIfNotUndefined("include");
@@ -2786,7 +2789,6 @@ namespace ts {
27862789
sourceFile: TsConfigSourceFile | undefined,
27872790
extendedConfigPath: string,
27882791
host: ParseConfigHost,
2789-
basePath: string,
27902792
resolutionStack: string[],
27912793
errors: Push<Diagnostic>,
27922794
extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry>
@@ -2801,25 +2803,8 @@ namespace ts {
28012803
else {
28022804
extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
28032805
if (!extendedResult.parseDiagnostics.length) {
2804-
const extendedDirname = getDirectoryPath(extendedConfigPath);
2805-
extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname,
2806+
extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, getDirectoryPath(extendedConfigPath),
28062807
getBaseFileName(extendedConfigPath), resolutionStack, errors, extendedConfigCache);
2807-
2808-
if (isSuccessfulParsedTsconfig(extendedConfig)) {
2809-
// Update the paths to reflect base path
2810-
const relativeDifference = convertToRelativePath(extendedDirname, basePath, identity);
2811-
const updatePath = (path: string) => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
2812-
const mapPropertiesInRawIfNotUndefined = (propertyName: string) => {
2813-
if (raw[propertyName]) {
2814-
raw[propertyName] = map(raw[propertyName], updatePath);
2815-
}
2816-
};
2817-
2818-
const { raw } = extendedConfig;
2819-
mapPropertiesInRawIfNotUndefined("include");
2820-
mapPropertiesInRawIfNotUndefined("exclude");
2821-
mapPropertiesInRawIfNotUndefined("files");
2822-
}
28232808
}
28242809
if (extendedConfigCache) {
28252810
extendedConfigCache.set(path, { extendedResult, extendedConfig });

src/testRunner/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"unittests/services/transpile.ts",
113113
"unittests/tsbuild/amdModulesWithOut.ts",
114114
"unittests/tsbuild/configFileErrors.ts",
115+
"unittests/tsbuild/configFileExtends.ts",
115116
"unittests/tsbuild/containerOnlyReferenced.ts",
116117
"unittests/tsbuild/declarationEmit.ts",
117118
"unittests/tsbuild/demo.ts",
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
namespace ts {
2+
describe("unittests:: tsbuild:: configFileExtends:: when tsconfig extends another config", () => {
3+
function getConfigExtendsWithIncludeFs() {
4+
return loadProjectFromFiles({
5+
"/src/tsconfig.json": JSON.stringify({
6+
references: [
7+
{ path: "./shared/tsconfig.json" },
8+
{ path: "./webpack/tsconfig.json" }
9+
],
10+
files: []
11+
}),
12+
"/src/shared/tsconfig-base.json": JSON.stringify({
13+
include: ["./typings-base/"]
14+
}),
15+
"/src/shared/typings-base/globals.d.ts": `type Unrestricted = any;`,
16+
"/src/shared/tsconfig.json": JSON.stringify({
17+
extends: "./tsconfig-base.json",
18+
compilerOptions: {
19+
composite: true,
20+
outDir: "../target-tsc-build/",
21+
rootDir: ".."
22+
},
23+
files: ["./index.ts"]
24+
}),
25+
"/src/shared/index.ts": `export const a: Unrestricted = 1;`,
26+
"/src/webpack/tsconfig.json": JSON.stringify({
27+
extends: "../shared/tsconfig-base.json",
28+
compilerOptions: {
29+
composite: true,
30+
outDir: "../target-tsc-build/",
31+
rootDir: ".."
32+
},
33+
files: ["./index.ts"],
34+
references: [{ path: "../shared/tsconfig.json" }]
35+
}),
36+
"/src/webpack/index.ts": `export const b: Unrestricted = 1;`,
37+
});
38+
}
39+
verifyTsc({
40+
scenario: "configFileExtends",
41+
subScenario: "when building solution with projects extends config with include",
42+
fs: getConfigExtendsWithIncludeFs,
43+
commandLineArgs: ["--b", "/src/tsconfig.json", "--v", "--listFiles"],
44+
});
45+
verifyTsc({
46+
scenario: "configFileExtends",
47+
subScenario: "when building project uses reference and both extend config with include",
48+
fs: getConfigExtendsWithIncludeFs,
49+
commandLineArgs: ["--b", "/src/webpack/tsconfig.json", "--v", "--listFiles"],
50+
});
51+
});
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
Input::
2+
//// [/lib/lib.d.ts]
3+
/// <reference no-default-lib="true"/>
4+
interface Boolean {}
5+
interface Function {}
6+
interface CallableFunction {}
7+
interface NewableFunction {}
8+
interface IArguments {}
9+
interface Number { toExponential: any; }
10+
interface Object {}
11+
interface RegExp {}
12+
interface String { charAt: any; }
13+
interface Array<T> { length: number; [n: number]: T; }
14+
interface ReadonlyArray<T> {}
15+
declare const console: { log(msg: any): void; };
16+
17+
//// [/src/shared/index.ts]
18+
export const a: Unrestricted = 1;
19+
20+
//// [/src/shared/tsconfig-base.json]
21+
{"include":["./typings-base/"]}
22+
23+
//// [/src/shared/tsconfig.json]
24+
{"extends":"./tsconfig-base.json","compilerOptions":{"composite":true,"outDir":"../target-tsc-build/","rootDir":".."},"files":["./index.ts"]}
25+
26+
//// [/src/shared/typings-base/globals.d.ts]
27+
type Unrestricted = any;
28+
29+
//// [/src/tsconfig.json]
30+
31+
32+
//// [/src/webpack/index.ts]
33+
export const b: Unrestricted = 1;
34+
35+
//// [/src/webpack/tsconfig.json]
36+
{"extends":"../shared/tsconfig-base.json","compilerOptions":{"composite":true,"outDir":"../target-tsc-build/","rootDir":".."},"files":["./index.ts"],"references":[{"path":"../shared/tsconfig.json"}]}
37+
38+
39+
40+
Output::
41+
/lib/tsc --b /src/webpack/tsconfig.json --v --listFiles
42+
[12:00:00 AM] Projects in this build:
43+
* src/shared/tsconfig.json
44+
* src/webpack/tsconfig.json
45+
46+
[12:00:00 AM] Project 'src/shared/tsconfig.json' is out of date because output file 'src/target-tsc-build/shared/index.js' does not exist
47+
48+
[12:00:00 AM] Building project '/src/shared/tsconfig.json'...
49+
50+
/lib/lib.d.ts
51+
/src/shared/index.ts
52+
/src/shared/typings-base/globals.d.ts
53+
[12:00:00 AM] Project 'src/webpack/tsconfig.json' is out of date because output file 'src/target-tsc-build/webpack/index.js' does not exist
54+
55+
[12:00:00 AM] Building project '/src/webpack/tsconfig.json'...
56+
57+
/lib/lib.d.ts
58+
/src/webpack/index.ts
59+
/src/shared/typings-base/globals.d.ts
60+
exitCode:: ExitStatus.Success
61+
62+
63+
//// [/src/target-tsc-build/shared/index.d.ts]
64+
export declare const a: Unrestricted;
65+
66+
67+
//// [/src/target-tsc-build/shared/index.js]
68+
"use strict";
69+
exports.__esModule = true;
70+
exports.a = void 0;
71+
exports.a = 1;
72+
73+
74+
//// [/src/target-tsc-build/shared/tsconfig.tsbuildinfo]
75+
{
76+
"program": {
77+
"fileInfos": {
78+
"../../../lib/lib.d.ts": {
79+
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
80+
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
81+
"affectsGlobalScope": true
82+
},
83+
"../../shared/index.ts": {
84+
"version": "-22125360210-export const a: Unrestricted = 1;",
85+
"signature": "-478734393-export declare const a: Unrestricted;\r\n",
86+
"affectsGlobalScope": false
87+
},
88+
"../../shared/typings-base/globals.d.ts": {
89+
"version": "4725476611-type Unrestricted = any;",
90+
"signature": "4725476611-type Unrestricted = any;",
91+
"affectsGlobalScope": true
92+
}
93+
},
94+
"options": {
95+
"composite": true,
96+
"outDir": "..",
97+
"rootDir": "../..",
98+
"listFiles": true,
99+
"configFilePath": "../../shared/tsconfig.json"
100+
},
101+
"referencedMap": {},
102+
"exportedModulesMap": {},
103+
"semanticDiagnosticsPerFile": [
104+
"../../../lib/lib.d.ts",
105+
"../../shared/index.ts",
106+
"../../shared/typings-base/globals.d.ts"
107+
]
108+
},
109+
"version": "FakeTSVersion"
110+
}
111+
112+
//// [/src/target-tsc-build/webpack/index.d.ts]
113+
export declare const b: Unrestricted;
114+
115+
116+
//// [/src/target-tsc-build/webpack/index.js]
117+
"use strict";
118+
exports.__esModule = true;
119+
exports.b = void 0;
120+
exports.b = 1;
121+
122+
123+
//// [/src/target-tsc-build/webpack/tsconfig.tsbuildinfo]
124+
{
125+
"program": {
126+
"fileInfos": {
127+
"../../../lib/lib.d.ts": {
128+
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
129+
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
130+
"affectsGlobalScope": true
131+
},
132+
"../../webpack/index.ts": {
133+
"version": "-14405273073-export const b: Unrestricted = 1;",
134+
"signature": "-5074241048-export declare const b: Unrestricted;\r\n",
135+
"affectsGlobalScope": false
136+
},
137+
"../../shared/typings-base/globals.d.ts": {
138+
"version": "4725476611-type Unrestricted = any;",
139+
"signature": "4725476611-type Unrestricted = any;",
140+
"affectsGlobalScope": true
141+
}
142+
},
143+
"options": {
144+
"composite": true,
145+
"outDir": "..",
146+
"rootDir": "../..",
147+
"listFiles": true,
148+
"configFilePath": "../../webpack/tsconfig.json"
149+
},
150+
"referencedMap": {},
151+
"exportedModulesMap": {},
152+
"semanticDiagnosticsPerFile": [
153+
"../../../lib/lib.d.ts",
154+
"../../shared/typings-base/globals.d.ts",
155+
"../../webpack/index.ts"
156+
]
157+
},
158+
"version": "FakeTSVersion"
159+
}
160+

0 commit comments

Comments
 (0)