evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
38.09k stars 1.14k forks source link

Could not resolve file without extension from node_modules when using ts and tsx loader for js files #3934

Open krtcom opened 2 weeks ago

krtcom commented 2 weeks ago

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.

hyrious commented 2 weeks ago

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]).