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.
The
rewrittenFileExtensionscomment says the order follows TypeScript:https://github.com/evanw/esbuild/blob/main/internal/resolver/resolver.go#L1723-L1730
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,
tryAddingExtensionshad no special case for.Jsx— it fell through todefaultwhich always tried.tsthen.tsx:In TypeScript 5.0 (PR #51435, merged
89e928e8b4, Jan 2023),tryAddingExtensionswas restructured for--allowArbitraryExtensions. As part of that refactor,.Jsxgot its own explicit case with reversed order:https://github.com/microsoft/TypeScript/blob/main/src/compiler/moduleNameResolver.ts#L2159-L2167
TypeScript's comment indicates this was intentional —
.jsximplies JSX, so.tsxis the closer match.The
rewrittenFileExtensionsmap hasn't been modified since the original 2020 commit. This only matters when bothfile.tsandfile.tsxexist for the same.jsximport — an unlikely scenario. Just flagging it since the comment references TypeScript's behavior.