Skip to content

Commit d5f397f

Browse files
committed
fix #3354: tsconfig.json regression with paths
1 parent 72b1e8b commit d5f397f

File tree

4 files changed

+87
-29
lines changed

4 files changed

+87
-29
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
* Fix a regression in 0.19.0 regarding `paths` in `tsconfig.json` ([#3354](https://github.com/evanw/esbuild/issues/3354))
6+
7+
The fix in esbuild version 0.19.0 to process `tsconfig.json` aliases before the `--packages=external` setting unintentionally broke an edge case in esbuild's handling of certain `tsconfig.json` aliases where there are multiple files with the same name in different directories. This release adjusts esbuild's behavior for this edge case so that it passes while still processing aliases before `--packages=external`. Please read the linked issue for more details.
8+
59
* Fix a CSS `font` property minification bug ([#3452](https://github.com/evanw/esbuild/issues/3452))
610

711
This release fixes a bug where esbuild's CSS minifier didn't insert a space between the font size and the font family in the `font` CSS shorthand property in the edge case where the original source code didn't already have a space and the leading string token was shortened to an identifier:

internal/bundler_tests/bundler_tsconfig_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2538,3 +2538,46 @@ func TestTsconfigJsonBaseUrlIssue3307(t *testing.T) {
25382538
},
25392539
})
25402540
}
2541+
2542+
// https://github.com/evanw/esbuild/issues/3354
2543+
func TestTsconfigJsonAsteriskNameCollisionIssue3354(t *testing.T) {
2544+
tsconfig_suite.expectBundled(t, bundled{
2545+
files: map[string]string{
2546+
"/Users/user/project/src/entry.ts": `
2547+
import {foo} from "foo";
2548+
foo();
2549+
`,
2550+
"/Users/user/project/src/tsconfig.json": `
2551+
{
2552+
"compilerOptions": {
2553+
"baseUrl": ".",
2554+
"paths": {
2555+
"*": ["web/*"]
2556+
}
2557+
}
2558+
}
2559+
`,
2560+
"/Users/user/project/src/web/foo.ts": `
2561+
import {foo as barFoo} from 'bar/foo';
2562+
export function foo() {
2563+
console.log('web/foo');
2564+
barFoo();
2565+
}
2566+
`,
2567+
"/Users/user/project/src/web/bar/foo/foo.ts": `
2568+
export function foo() {
2569+
console.log('bar/foo');
2570+
}
2571+
`,
2572+
"/Users/user/project/src/web/bar/foo/index.ts": `
2573+
export {foo} from './foo'
2574+
`,
2575+
},
2576+
entryPaths: []string{"entry.ts"},
2577+
absWorkingDir: "/Users/user/project/src",
2578+
options: config.Options{
2579+
Mode: config.ModeBundle,
2580+
AbsOutputFile: "/Users/user/project/out.js",
2581+
},
2582+
})
2583+
}

internal/bundler_tests/snapshots/snapshots_tsconfig.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,23 @@ var require_util = __commonJS({
206206
var import_util = __toESM(require_util());
207207
console.log((0, import_util.default)());
208208

209+
================================================================================
210+
TestTsconfigJsonAsteriskNameCollisionIssue3354
211+
---------- /Users/user/project/out.js ----------
212+
// web/bar/foo/foo.ts
213+
function foo() {
214+
console.log("bar/foo");
215+
}
216+
217+
// web/foo.ts
218+
function foo2() {
219+
console.log("web/foo");
220+
foo();
221+
}
222+
223+
// entry.ts
224+
foo2();
225+
209226
================================================================================
210227
TestTsconfigJsonBaseUrl
211228
---------- /Users/user/project/out.js ----------

internal/resolver/resolver.go

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -993,36 +993,12 @@ func (r resolverQuery) resolveWithoutSymlinks(sourceDir string, sourceDirInfo *d
993993
}
994994
}
995995

996-
// First, check path overrides from the nearest enclosing TypeScript "tsconfig.json" file
997-
if sourceDirInfo != nil {
998-
if tsConfigJSON := r.tsConfigForDir(sourceDirInfo); tsConfigJSON != nil && tsConfigJSON.Paths != nil {
999-
if absolute, ok, diffCase := r.matchTSConfigPaths(tsConfigJSON, importPath); ok {
1000-
return &ResolveResult{PathPair: absolute, DifferentCase: diffCase}
1001-
}
1002-
}
1003-
}
1004-
1005996
// Check both relative and package paths for CSS URL tokens, with relative
1006997
// paths taking precedence over package paths to match Webpack behavior.
1007998
isPackagePath := IsPackagePath(importPath)
1008999
checkRelative := !isPackagePath || r.kind.IsFromCSS()
10091000
checkPackage := isPackagePath
10101001

1011-
// "import 'pkg'" when all packages are external (vs. "import './pkg'"). This
1012-
// is deliberately done after we check for "tsconfig.json" aliases because
1013-
// people want to be able to make things look like packages but have them not
1014-
// be packages.
1015-
if r.options.ExternalPackages && isPackagePath && !strings.HasPrefix(importPath, "#") {
1016-
if r.debugLogs != nil {
1017-
r.debugLogs.addNote("Marking this path as external because it's a package path")
1018-
}
1019-
1020-
r.flushDebugLogs(flushDueToSuccess)
1021-
return &ResolveResult{
1022-
PathPair: PathPair{Primary: logger.Path{Text: importPath}, IsExternal: true},
1023-
}
1024-
}
1025-
10261002
if checkRelative {
10271003
absPath := r.fs.Join(sourceDir, importPath)
10281004

@@ -2301,11 +2277,21 @@ func (r resolverQuery) loadNodeModules(importPath string, dirInfo *dirInfo, forb
23012277
defer r.debugLogs.decreaseIndent()
23022278
}
23032279

2304-
// Try looking up the path relative to the base URL from "tsconfig.json"
2305-
if tsConfigJSON := r.tsConfigForDir(dirInfo); tsConfigJSON != nil && tsConfigJSON.BaseURL != nil {
2306-
basePath := r.fs.Join(*tsConfigJSON.BaseURL, importPath)
2307-
if absolute, ok, diffCase := r.loadAsFileOrDirectory(basePath); ok {
2308-
return absolute, true, diffCase
2280+
// First, check path overrides from the nearest enclosing TypeScript "tsconfig.json" file
2281+
if tsConfigJSON := r.tsConfigForDir(dirInfo); tsConfigJSON != nil {
2282+
// Try path substitutions first
2283+
if tsConfigJSON.Paths != nil {
2284+
if absolute, ok, diffCase := r.matchTSConfigPaths(tsConfigJSON, importPath); ok {
2285+
return absolute, true, diffCase
2286+
}
2287+
}
2288+
2289+
// Try looking up the path relative to the base URL
2290+
if tsConfigJSON.BaseURL != nil {
2291+
basePath := r.fs.Join(*tsConfigJSON.BaseURL, importPath)
2292+
if absolute, ok, diffCase := r.loadAsFileOrDirectory(basePath); ok {
2293+
return absolute, true, diffCase
2294+
}
23092295
}
23102296
}
23112297

@@ -2320,6 +2306,14 @@ func (r resolverQuery) loadNodeModules(importPath string, dirInfo *dirInfo, forb
23202306
return r.loadPackageImports(importPath, dirInfoPackageJSON)
23212307
}
23222308

2309+
// "import 'pkg'" when all packages are external (vs. "import './pkg'")
2310+
if r.options.ExternalPackages && IsPackagePath(importPath) {
2311+
if r.debugLogs != nil {
2312+
r.debugLogs.addNote("Marking this path as external because it's a package path")
2313+
}
2314+
return PathPair{Primary: logger.Path{Text: importPath}, IsExternal: true}, true, nil
2315+
}
2316+
23232317
// If Yarn PnP is active, use it to find the package
23242318
if r.pnpManifest != nil {
23252319
if result := r.resolveToUnqualified(importPath, dirInfo.absPath, r.pnpManifest); result.status.isError() {

0 commit comments

Comments
 (0)