diff --git a/tsc/internal/tsoptions/tsconfigparsing.go b/tsc/internal/tsoptions/tsconfigparsing.go index 0490a5542b4a7..cb4c7d01d723d 100644 --- a/tsc/internal/tsoptions/tsconfigparsing.go +++ b/tsc/internal/tsoptions/tsconfigparsing.go @@ -1012,7 +1012,7 @@ func parseConfig( var errors []*ast.Diagnostic if slices.Contains(resolutionStack, resolvedPath) { var result *parsedTsconfig - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Circularity_detected_while_resolving_configuration_Colon_0)) + errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, strings.Join(append(core.Map(resolutionStack[slices.Index(resolutionStack, resolvedPath):], tspath.PathKey.AsString), resolvedPath.AsString()), " -> "))) if json.Size() == 0 { result = &parsedTsconfig{raw: json} } else { diff --git a/tsc/internal/tsoptions/tsconfigparsing_test.go b/tsc/internal/tsoptions/tsconfigparsing_test.go index 1eb344588c20a..7d9fa696bcb20 100644 --- a/tsc/internal/tsoptions/tsconfigparsing_test.go +++ b/tsc/internal/tsoptions/tsconfigparsing_test.go @@ -1067,6 +1067,40 @@ func TestParseJsonSourceFileConfigFileContentReportsInvalidExtendedConfig(t *tes } } +func TestParseJsonSourceFileConfigFileContentReportsCircularExtendsChain(t *testing.T) { + t.Parallel() + files := map[string]string{ + "/project/tsconfig.json": `{ "extends": "./a.json" }`, + "/project/a.json": `{ "extends": "./b.json" }`, + "/project/b.json": `{ "extends": "./a.json" }`, + "/project/main.ts": "export const x = 1;", + } + fs := tsoptionstest.NewVFS(files, tspath.CaseSensitive /*caseSensitivity*/) + configFileName := tspath.RootedFilePath("/project/tsconfig.json") + configFile := tsoptions.NewTsconfigSourceFileFromFilePath( + configFileName, + fs.CaseSensitivity().PathKey(tspath.RootedPath(configFileName)), + files[configFileName.AsString()], + ) + + parsed := tsoptions.ParseJsonSourceFileConfigFileContent( + configFile, + tsoptions.NewDefaultParseConfigHost(fs), + "/project", + nil, + nil, + nil, + nil, + ) + + circularity := core.Filter(parsed.Errors, func(diagnostic *ast.Diagnostic) bool { + return diagnostic.Code() == diagnostics.Circularity_detected_while_resolving_configuration_Colon_0.Code() + }) + assert.DeepEqual(t, core.Map(circularity, (*ast.Diagnostic).MessageArgs), [][]string{ + {"/project/a.json -> /project/b.json -> /project/a.json"}, + }) +} + // Extending an empty config file used to panic on nil Statements (#4265). func TestParseJsonSourceFileConfigFileContentWithEmptyExtendedConfig(t *testing.T) { t.Parallel()