Skip to content

Commit 9415beb

Browse files
authored
Fix panic on non-string include/exclude/files in extended configs (#4384)
1 parent a844752 commit 9415beb

6 files changed

Lines changed: 176 additions & 3 deletions

File tree

internal/execute/tsctests/tsc_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,13 +1071,53 @@ func TestTscExtends(t *testing.T) {
10711071
edits: edits,
10721072
}
10731073
}
1074+
getTscExtendsNonStringPathTestCase := func(propertyName string) *tscInput {
1075+
return &tscInput{
1076+
subScenario: "extends config with non-string " + propertyName,
1077+
files: FileMap{
1078+
"/home/src/projects/project/tsconfig.json": stringtestutil.Dedent(`
1079+
{
1080+
"extends": "./base.json",
1081+
}`),
1082+
"/home/src/projects/project/base.json": stringtestutil.Dedent(`
1083+
{
1084+
"` + propertyName + `": [1],
1085+
}`),
1086+
"/home/src/projects/project/main.ts": `export const x = 1;`,
1087+
},
1088+
cwd: "/home/src/projects/project",
1089+
commandLineArgs: []string{"-p", "tsconfig.json", "--pretty", "false"},
1090+
}
1091+
}
1092+
getTscExtendsBase := func(baseContents string) FileMap {
1093+
return FileMap{
1094+
"/home/src/projects/project/tsconfig.json": stringtestutil.Dedent(`
1095+
{
1096+
"extends": "./base.json",
1097+
}`),
1098+
"/home/src/projects/project/base.json": stringtestutil.Dedent(baseContents),
1099+
"/home/src/projects/project/main.ts": `export const x = 1;`,
1100+
}
1101+
}
10741102
testCases := []*tscInput{
10751103
{
10761104
subScenario: "when building solution with projects extends config with include",
10771105
files: getBuildConfigFileExtendsFileMap(),
10781106
cwd: "/home/src/workspaces/solution",
10791107
commandLineArgs: []string{"--b", "--v", "--listFiles"},
10801108
},
1109+
getTscExtendsNonStringPathTestCase("include"),
1110+
getTscExtendsNonStringPathTestCase("exclude"),
1111+
getTscExtendsNonStringPathTestCase("files"),
1112+
{
1113+
subScenario: "extends config with mixed valid and non-string include",
1114+
files: getTscExtendsBase(`
1115+
{
1116+
"include": ["main.ts", 1],
1117+
}`),
1118+
cwd: "/home/src/projects/project",
1119+
commandLineArgs: []string{"-p", "tsconfig.json", "--pretty", "false"},
1120+
},
10811121
{
10821122
subScenario: "when building project uses reference and both extend config with include",
10831123
files: getBuildConfigFileExtendsFileMap(),

internal/tsoptions/tsconfigparsing.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,8 +1091,12 @@ func parseConfig(
10911091
if rawMap, ok := extendsRaw.(*collections.OrderedMap[string, any]); ok && rawMap.Has(propertyName) {
10921092
if slice, _ := rawMap.GetOrZero(propertyName).([]any); slice != nil {
10931093
value := core.Map(slice, func(path any) any {
1094-
if startsWithConfigDirTemplate(path) || tspath.IsRootedDiskPath(path.(string)) {
1095-
return path.(string)
1094+
pathStr, isString := path.(string)
1095+
if !isString {
1096+
return path
1097+
}
1098+
if startsWithConfigDirTemplate(path) || tspath.IsRootedDiskPath(pathStr) {
1099+
return pathStr
10961100
} else {
10971101
if relativeDifference == "" {
10981102
t := tspath.ComparePathsOptions{
@@ -1101,7 +1105,7 @@ func parseConfig(
11011105
}
11021106
relativeDifference = tspath.ConvertToRelativePath(tspath.GetDirectoryPath(extendedConfigPath), t)
11031107
}
1104-
return tspath.CombinePaths(relativeDifference, path.(string))
1108+
return tspath.CombinePaths(relativeDifference, pathStr)
11051109
}
11061110
})
11071111
if propertyName == "include" {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
currentDirectory::/home/src/projects/project
2+
useCaseSensitiveFileNames::true
3+
Input::
4+
//// [/home/src/projects/project/base.json] *new*
5+
{
6+
"include": ["main.ts", 1],
7+
}
8+
//// [/home/src/projects/project/main.ts] *new*
9+
export const x = 1;
10+
//// [/home/src/projects/project/tsconfig.json] *new*
11+
{
12+
"extends": "./base.json",
13+
}
14+
15+
tsgo -p tsconfig.json --pretty false
16+
ExitStatus:: DiagnosticsPresent_OutputsGenerated
17+
Output::
18+
base.json(2,28): error TS5024: Compiler option 'include' requires a value of type string.
19+
//// [/home/src/projects/project/main.js] *new*
20+
export const x = 1;
21+
22+
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
23+
/// <reference no-default-lib="true"/>
24+
interface Boolean {}
25+
interface Function {}
26+
interface CallableFunction {}
27+
interface NewableFunction {}
28+
interface IArguments {}
29+
interface Number { toExponential: any; }
30+
interface Object {}
31+
interface RegExp {}
32+
interface String { charAt: any; }
33+
interface Array<T> { length: number; [n: number]: T; }
34+
interface ReadonlyArray<T> {}
35+
interface SymbolConstructor {
36+
(desc?: string | number): symbol;
37+
for(name: string): symbol;
38+
readonly toStringTag: symbol;
39+
}
40+
declare var Symbol: SymbolConstructor;
41+
interface Symbol {
42+
readonly [Symbol.toStringTag]: string;
43+
}
44+
declare const console: { log(msg: any): void; };
45+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
currentDirectory::/home/src/projects/project
2+
useCaseSensitiveFileNames::true
3+
Input::
4+
//// [/home/src/projects/project/base.json] *new*
5+
{
6+
"exclude": [1],
7+
}
8+
//// [/home/src/projects/project/main.ts] *new*
9+
export const x = 1;
10+
//// [/home/src/projects/project/tsconfig.json] *new*
11+
{
12+
"extends": "./base.json",
13+
}
14+
15+
tsgo -p tsconfig.json --pretty false
16+
ExitStatus:: DiagnosticsPresent_OutputsGenerated
17+
Output::
18+
base.json(2,17): error TS5024: Compiler option 'exclude' requires a value of type string.
19+
//// [/home/src/projects/project/main.js] *new*
20+
export const x = 1;
21+
22+
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
23+
/// <reference no-default-lib="true"/>
24+
interface Boolean {}
25+
interface Function {}
26+
interface CallableFunction {}
27+
interface NewableFunction {}
28+
interface IArguments {}
29+
interface Number { toExponential: any; }
30+
interface Object {}
31+
interface RegExp {}
32+
interface String { charAt: any; }
33+
interface Array<T> { length: number; [n: number]: T; }
34+
interface ReadonlyArray<T> {}
35+
interface SymbolConstructor {
36+
(desc?: string | number): symbol;
37+
for(name: string): symbol;
38+
readonly toStringTag: symbol;
39+
}
40+
declare var Symbol: SymbolConstructor;
41+
interface Symbol {
42+
readonly [Symbol.toStringTag]: string;
43+
}
44+
declare const console: { log(msg: any): void; };
45+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
currentDirectory::/home/src/projects/project
2+
useCaseSensitiveFileNames::true
3+
Input::
4+
//// [/home/src/projects/project/base.json] *new*
5+
{
6+
"files": [1],
7+
}
8+
//// [/home/src/projects/project/main.ts] *new*
9+
export const x = 1;
10+
//// [/home/src/projects/project/tsconfig.json] *new*
11+
{
12+
"extends": "./base.json",
13+
}
14+
15+
tsgo -p tsconfig.json --pretty false
16+
ExitStatus:: DiagnosticsPresent_OutputsGenerated
17+
Output::
18+
base.json(2,15): error TS5024: Compiler option 'files' requires a value of type string.
19+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
currentDirectory::/home/src/projects/project
2+
useCaseSensitiveFileNames::true
3+
Input::
4+
//// [/home/src/projects/project/base.json] *new*
5+
{
6+
"include": [1],
7+
}
8+
//// [/home/src/projects/project/main.ts] *new*
9+
export const x = 1;
10+
//// [/home/src/projects/project/tsconfig.json] *new*
11+
{
12+
"extends": "./base.json",
13+
}
14+
15+
tsgo -p tsconfig.json --pretty false
16+
ExitStatus:: DiagnosticsPresent_OutputsGenerated
17+
Output::
18+
error TS18003: No inputs were found in config file '/home/src/projects/project/tsconfig.json'. Specified 'include' paths were '[1]' and 'exclude' paths were '[]'.
19+
base.json(2,17): error TS5024: Compiler option 'include' requires a value of type string.
20+

0 commit comments

Comments
 (0)