Skip to content

Commit

Permalink
fix #3898: incorrect cyclic tsconfig.json warning
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Sep 21, 2024
1 parent b500443 commit 46fdb68
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
./esbuild --version
```

* Avoid incorrect cycle warning with `tsconfig.json` multiple inheritance ([#3898](https://github.com/evanw/esbuild/issues/3898))

TypeScript 5.0 introduced multiple inheritance for `tsconfig.json` files where `extends` can be an array of file paths. Previously esbuild would incorrectly treat files encountered more than once when processing separate subtrees of the multiple inheritance hierarchy as an inheritance cycle. With this release, `tsconfig.json` files containing this edge case should work correctly without generating a warning.

* Handle Yarn Plug'n'Play stack overflow with `tsconfig.json` ([#3915](https://github.com/evanw/esbuild/issues/3915))

Previously a `tsconfig.json` file that `extends` another file in a package with an `exports` map could cause a stack overflow when Yarn's Plug'n'Play resolution was active. This edge case should work now starting with this release.
Expand Down
52 changes: 52 additions & 0 deletions internal/bundler_tests/bundler_tsconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2817,3 +2817,55 @@ func TestTsconfigStackOverflowYarnPnP(t *testing.T) {
},
})
}

func TestTsconfigJsonExtendsArrayIssue3898(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/index.tsx": `
import { type SomeType } from 'MUST_KEEP'
console.log(<>
<div/>
</>)
`,
"/Users/user/project/tsconfig.json": `
{
"extends": [
"./tsconfigs/a.json",
"./tsconfigs/b.json",
]
}
`,
"/Users/user/project/tsconfigs/base.json": `
{
"compilerOptions": {
"verbatimModuleSyntax": true,
}
}
`,
"/Users/user/project/tsconfigs/a.json": `
{
"extends": "./base.json",
"compilerOptions": {
"jsxFactory": "SUCCESS",
}
}
`,
"/Users/user/project/tsconfigs/b.json": `
{
"extends": "./base.json",
"compilerOptions": {
"jsxFragmentFactory": "WORKS",
}
}
`,
},
entryPaths: []string{"/Users/user/project/index.tsx"},
options: config.Options{
Mode: config.ModePassThrough,
AbsOutputFile: "/Users/user/project/out.js",
JSX: config.JSXOptions{
SideEffects: true,
},
},
})
}
6 changes: 6 additions & 0 deletions internal/bundler_tests/snapshots/snapshots_tsconfig.txt
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ TestTsconfigJsonExtendsAbsolute
// Users/user/project/entry.jsx
console.log(/* @__PURE__ */ baseFactory("div", null), /* @__PURE__ */ baseFactory(derivedFragment, null));

================================================================================
TestTsconfigJsonExtendsArrayIssue3898
---------- /Users/user/project/out.js ----------
import {} from "MUST_KEEP";
console.log(SUCCESS(WORKS, null, SUCCESS("div", null)));

================================================================================
TestTsconfigJsonExtendsLoop
---------- /out.js ----------
Expand Down
20 changes: 14 additions & 6 deletions internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1183,11 +1183,6 @@ func (r resolverQuery) parseTSConfig(file string, visited map[string]bool, confi
if visited[file] {
return nil, errParseErrorImportCycle
}
if visited != nil {
// This is only non-nil for "build" API calls. This is nil for "transform"
// API calls, which tells us to not process "extends" fields.
visited[file] = true
}

contents, err, originalError := r.caches.FSCache.ReadFile(r.fs, file)
if r.debugLogs != nil && originalError != nil {
Expand All @@ -1206,7 +1201,20 @@ func (r resolverQuery) parseTSConfig(file string, visited map[string]bool, confi
PrettyPath: PrettyPath(r.fs, keyPath),
Contents: contents,
}
return r.parseTSConfigFromSource(source, visited, configDir)
if visited != nil {
// This is only non-nil for "build" API calls. This is nil for "transform"
// API calls, which tells us to not process "extends" fields.
visited[file] = true
}
result, err := r.parseTSConfigFromSource(source, visited, configDir)
if visited != nil {
// Reset this to back false in case something uses TypeScript 5.0's multiple
// inheritance feature for "tsconfig.json" files. It should be valid to visit
// the same base "tsconfig.json" file multiple times from different multiple
// inheritance subtrees.
visited[file] = false
}
return result, err
}

func (r resolverQuery) parseTSConfigFromSource(source logger.Source, visited map[string]bool, configDir string) (*TSConfigJSON, error) {
Expand Down

0 comments on commit 46fdb68

Please sign in to comment.