Could not resolve file without extension from node_modules when using ts and tsx loader for js files
I have a very specific problem regarding ts and tsx loader and imports from node_modules that do not have extension specified. Steps or files to reproduce:
package.json:
{
"name": "esbuild-test",
"main": "app.js",
"scripts": {
"build": "node build.mjs"
},
"devDependencies": {
"esbuild": "^0.24.0"
},
"dependencies": {
"core-js": "^3.38.1",
"regenerator-runtime": "^0.14.1"
}
}
app.jsx:
import 'core-js/stable'
//import 'regenerator-runtime/runtime'
console.log('Hello world!');
build.mjs:
import esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.jsx'],
outfile: 'output.js',
bundle: true,
loader: {
'.jsx': 'tsx',
'.js': 'ts',
},
})
When I run node build.mjs I get this error: Could not resolve "core-js/stable".
Seems like a problem with resolving file index.js in core-js/stable folder. If I specify this file directly it works, but then it fails to resolve other imports form this file.
Likewise, if I import a file from node_modules without extension - in my case it was import 'regenerator-runtime/runtime' - the build fails with Could not resolve "regenerator-runtime/runtime" but if I provide full file name with extension import 'regenerator-runtime/runtime.js', it works.
By gradual elimination and trial-error method I discovered, that it may have something to do with tsx and ts loader. If I comment out either '.jsx': 'tsx' or '.js': 'ts' line from build.mjs the resolution and build works.
However, I need tsx transformation, because of experimental decorators that we are using through all of our project.
This also happes when I import some library which has a file without extension in its "main" field in package.json
(eg. "main": "es/index")
Interestingly, this setup was working in esbuild 0.19.* and stopped working in 0.20 (tested also all newer versions but without success). So I assume it may have something to do with 'Reorder implicit file extensions within node_modules' change in 0.20.0
I also tested multiple node versions 19, 20, 21, 22 all with the same results.
Please, can you confirm if this is a bug or just some incorrect setting, in which case please point me to the right direction.
It seems to only happen with loader: { '.js': 'ts' }. I'm not sure what insane project you're facing where people write TypeScript in .js files. However, you can check what's going on for all resolver errors with logLevel: 'verbose'.
Look at the difference between whether to set '.js': 'ts':
...
Attempting to load "path/to/project/node_modules/core-js/stable" as a file
Checking for file "stable"
+ Checking for file "stable.js"
+ Checking for file "stable.tsx"
+ Checking for file "stable.ts"
+ Checking for file "stable.jsx"
+ Checking for file "stable.css"
+ Checking for file "stable.json"
Failed to find file "stable"
...
It seems in that case esbuild won't try to resolve arbitrary file extensions (including index[ext]).