Skip to content

Commit

Permalink
fix #3767: tsconfig.json files inside symlinks
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 15, 2024
1 parent b7dcb95 commit 6e6f15f
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

* Fix `tsconfig.json` files inside symlinked directories ([#3767](https://github.com/evanw/esbuild/issues/3767))

This release fixes an issue with a scenario involving a `tsconfig.json` file that `extends` another file from within a symlinked directory that uses the `paths` feature. In that case, the implicit `baseURL` value should be based on the real path (i.e. after expanding all symbolic links) instead of the original path. This was already done for other files that esbuild resolves but was not yet done for `tsconfig.json` because it's special-cased (the regular path resolver can't be used because the information inside `tsconfig.json` is involved in path resolution). Note that this fix no longer applies if the `--preserve-symlinks` setting is enabled.

## 0.21.2

* Correct `this` in field and accessor decorators ([#3761](https://github.com/evanw/esbuild/issues/3761))
Expand Down
1 change: 1 addition & 0 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ type FS interface {
Join(parts ...string) string
Cwd() string
Rel(base string, target string) (string, bool)
EvalSymlinks(path string) (string, bool)

// This is used in the implementation of "Entry"
kind(dir string, base string) (symlink string, kind EntryKind)
Expand Down
4 changes: 4 additions & 0 deletions internal/fs/fs_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ func (fs *mockFS) Rel(base string, target string) (string, bool) {
return target, true
}

func (fs *mockFS) EvalSymlinks(path string) (string, bool) {
return "", false
}

func (fs *mockFS) kind(dir string, base string) (symlink string, kind EntryKind) {
panic("This should never be called")
}
Expand Down
7 changes: 7 additions & 0 deletions internal/fs/fs_real.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,13 @@ func (fs *realFS) Rel(base string, target string) (string, bool) {
return "", false
}

func (fs *realFS) EvalSymlinks(path string) (string, bool) {
if path, err := fs.fp.evalSymlinks(path); err == nil {
return path, true
}
return "", false
}

func (fs *realFS) readdir(dirname string) (entries []string, canonicalError error, originalError error) {
BeforeFileOpen()
defer AfterFileClose()
Expand Down
4 changes: 4 additions & 0 deletions internal/fs/fs_zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ func (fs *zipFS) Rel(base string, target string) (string, bool) {
return fs.inner.Rel(base, target)
}

func (fs *zipFS) EvalSymlinks(path string) (string, bool) {
return fs.inner.EvalSymlinks(path)
}

func (fs *zipFS) kind(dir string, base string) (symlink string, kind EntryKind) {
return fs.inner.kind(dir, base)
}
Expand Down
7 changes: 7 additions & 0 deletions internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,13 @@ var errParseErrorAlreadyLogged = errors.New("(error already logged)")
// Nested calls may also return "parseErrorImportCycle". In that case the
// caller is responsible for logging an appropriate error message.
func (r resolverQuery) parseTSConfig(file string, visited map[string]bool) (*TSConfigJSON, error) {
// Resolve any symlinks first before parsing the file
if !r.options.PreserveSymlinks {
if real, ok := r.fs.EvalSymlinks(file); ok {
file = real
}
}

// Don't infinite loop if a series of "extends" links forms a cycle
if visited[file] {
return nil, errParseErrorImportCycle
Expand Down
24 changes: 24 additions & 0 deletions scripts/end-to-end-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,30 @@ tests.push(
export let bar = 'bar'
`,
}),

// See: https://github.com/evanw/esbuild/issues/3767
test(['apps/client/src/index.ts', '--bundle', '--outfile=node.js'], {
'apps/client/src/index.ts': `
import { foo } from '~/foo'
if (foo !== 'foo') throw 'fail'
`,
'apps/client/src/foo.ts': `
export const foo = 'foo'
`,
'apps/client/tsconfig.json': `{
"extends": "@repo/tsconfig/base"
}`,
'apps/client/node_modules/@repo/tsconfig': {
symlink: `../../../../tooling/typescript`,
},
'tooling/typescript/base.json': `{
"compilerOptions": {
"paths": {
"~/*": ["../../apps/client/src/*"]
}
}
}`,
}),
)

// Test coverage for a special JSX error message
Expand Down

0 comments on commit 6e6f15f

Please sign in to comment.