Skip to content

.jsx rewriting order diverges from TypeScript 5.0+ #4398

@privatenumber

Description

@privatenumber

The rewrittenFileExtensions comment says the order follows TypeScript:

https://github.com/evanw/esbuild/blob/main/internal/resolver/resolver.go#L1723-L1730

// Note that the official compiler code always tries ".ts" before // ".tsx" even if the original extension was ".jsx". ".jsx": {".ts", ".tsx"},

This was added in 0cdc005 (Aug 2020, "fix for #118: replace js/jsx with ts/tsx like tsc") and was accurate for TypeScript at that time.

In TypeScript ≤4.x, tryAddingExtensions had no special case for .Jsx — it fell through to default which always tried .ts then .tsx:

// TypeScript ≤4.x — src/compiler/moduleNameResolver.ts // .Jsx had no explicit case, so it fell through to default: case Extension.Ts: case Extension.Tsx: case Extension.Dts: // falls through default: return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx)

In TypeScript 5.0 (PR #51435, merged 89e928e8b4, Jan 2023), tryAddingExtensions was restructured for --allowArbitraryExtensions. As part of that refactor, .Jsx got its own explicit case with reversed order:

https://github.com/microsoft/TypeScript/blob/main/src/compiler/moduleNameResolver.ts#L2159-L2167

// TypeScript 5.0+ — src/compiler/moduleNameResolver.ts case Extension.Tsx: case Extension.Jsx: // basically identical to the ts/js case below, but prefers matching // tsx and jsx files exactly before falling back to the ts or js file path return tryExtension(Extension.Tsx, ...) || tryExtension(Extension.Ts, ...)

TypeScript's comment indicates this was intentional — .jsx implies JSX, so .tsx is the closer match.

The rewrittenFileExtensions map hasn't been modified since the original 2020 commit. This only matters when both file.ts and file.tsx exist for the same .jsx import — an unlikely scenario. Just flagging it since the comment references TypeScript's behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions