Skip to content

Commit

Permalink
copy a typescript compiler tsconfig extends bug
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Dec 4, 2022
1 parent 8821fbe commit b9e9513
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 3 deletions.
108 changes: 108 additions & 0 deletions internal/bundler/bundler_tsconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2037,3 +2037,111 @@ func TestTsConfigAlwaysStrictTrueEmitDirectiveBundleESM(t *testing.T) {
},
})
}

func TestTsConfigExtendsDotWithoutSlash(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/main.ts": `
console.log(123n)
`,
"/Users/user/project/src/foo.json": `{
"extends": "."
}`,
"/Users/user/project/src/tsconfig.json": `{
"compilerOptions": {
"target": "ES6"
}
}`,
},
entryPaths: []string{"/Users/user/project/src/main.ts"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputDir: "/Users/user/project/out",
OutputFormat: config.FormatESModule,
TsConfigOverride: "/Users/user/project/src/foo.json",
},
expectedScanLog: `Users/user/project/src/main.ts: ERROR: Big integer literals are not available in the configured target environment ("ES6")
Users/user/project/src/tsconfig.json: NOTE: The target environment was set to "ES6" here:
`,
})
}

func TestTsConfigExtendsDotDotWithoutSlash(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/main.ts": `
console.log(123n)
`,
"/Users/user/project/src/tsconfig.json": `{
"extends": ".."
}`,
"/Users/user/project/tsconfig.json": `{
"compilerOptions": {
"target": "ES6"
}
}`,
},
entryPaths: []string{"/Users/user/project/src/main.ts"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputDir: "/Users/user/project/out",
OutputFormat: config.FormatESModule,
},
expectedScanLog: `Users/user/project/src/main.ts: ERROR: Big integer literals are not available in the configured target environment ("ES6")
Users/user/project/tsconfig.json: NOTE: The target environment was set to "ES6" here:
`,
})
}

func TestTsConfigExtendsDotWithSlash(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/main.ts": `
console.log(123n)
`,
"/Users/user/project/src/foo.json": `{
"extends": "./"
}`,
"/Users/user/project/src/tsconfig.json": `{
"compilerOptions": {
"target": "ES6"
}
}`,
},
entryPaths: []string{"/Users/user/project/src/main.ts"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputDir: "/Users/user/project/out",
OutputFormat: config.FormatESModule,
TsConfigOverride: "/Users/user/project/src/foo.json",
},
expectedScanLog: `Users/user/project/src/foo.json: WARNING: Cannot find base config file "./"
`,
})
}

func TestTsConfigExtendsDotDotWithSlash(t *testing.T) {
tsconfig_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/main.ts": `
console.log(123n)
`,
"/Users/user/project/src/tsconfig.json": `{
"extends": "../"
}`,
"/Users/user/project/tsconfig.json": `{
"compilerOptions": {
"target": "ES6"
}
}`,
},
entryPaths: []string{"/Users/user/project/src/main.ts"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputDir: "/Users/user/project/out",
OutputFormat: config.FormatESModule,
},
expectedScanLog: `Users/user/project/src/tsconfig.json: WARNING: Cannot find base config file "../"
`,
})
}
12 changes: 12 additions & 0 deletions internal/bundler/snapshots/snapshots_tsconfig.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ var foo = 123;
// Users/user/project/src/entry.ts
console.log(foo);

================================================================================
TestTsConfigExtendsDotDotWithSlash
---------- /Users/user/project/out/main.js ----------
// Users/user/project/src/main.ts
console.log(123n);

================================================================================
TestTsConfigExtendsDotWithSlash
---------- /Users/user/project/out/main.js ----------
// Users/user/project/src/main.ts
console.log(123n);

================================================================================
TestTsConfigJSX
---------- /Users/user/project/out.js ----------
Expand Down
24 changes: 21 additions & 3 deletions internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1095,10 +1095,28 @@ func (r resolverQuery) parseTSConfig(file string, visited map[string]bool) (*TSC
current = next
}
} else {
// If this is a regular path, search relative to the enclosing directory
extendsFile := extends
if !r.fs.IsAbs(extends) {
extendsFile = r.fs.Join(fileDir, extends)

// The TypeScript compiler has a strange behavior that seems like a bug
// where "." and ".." behave differently than other forms such as "./."
// or "../." and are interpreted as having an implicit "tsconfig.json"
// suffix.
//
// I believe their bug is caused by some parts of their code checking for
// relative paths using the literal "./" and "../" prefixes (requiring
// the slash) and other parts checking using the regular expression
// /^\.\.?($|[\\/])/ (with the slash optional).
//
// In any case, people are now relying on this behavior. One example is
// this: https://github.com/esbuild-kit/tsx/pull/158. So we replicate this
// bug in esbuild as well.
if extendsFile == "." || extendsFile == ".." {
extendsFile += "/tsconfig.json"
}

// If this is a regular path, search relative to the enclosing directory
if !r.fs.IsAbs(extendsFile) {
extendsFile = r.fs.Join(fileDir, extendsFile)
}
base, err := r.parseTSConfig(extendsFile, visited)

Expand Down

0 comments on commit b9e9513

Please sign in to comment.