esbuild icon indicating copy to clipboard operation
esbuild copied to clipboard

esbuild does not use typescript source for self-references or sub path imports

Open everett1992 opened this issue 2 years ago • 2 comments

Describe the bug

Subpath and self-reference imports are resolved to their literal values rather than the TypeScript source file.

  // package.json
{
  "name": "subpath-exports",
  "exports": {
    "./sub": "./dist/sub.js"
  },
  "imports": {
    "#sub": "./dist/sub.js"
  }
}
// src/index.ts
import "./sub.js"
import "subpath-exports/sub"
import "#sub"

esbuild will resolve #sub and subpath-exports/sub to ./dist/sub.js, not ./src/sub.ts. This can cause module not found errors if dist has not been produced, or unexpected behavior if the dist/ is out of date and doesn't match the other source.

Reproduction

echo 'import("#sub"); import("subpath-exports/sub")' | npx esbuild --bundle
✘ [ERROR] Could not resolve "#sub"

    <stdin>:1:7:
      1 │ import("#sub"); import("subpath-exports/sub")
        ╵        ~~~~~~

  The module "./dist/sub.js" was not found on the file system:

    package.json:9:12:
      9 │     "#sub": "./dist/sub.js"
        ╵             ~~~~~~~~~~~~~~~

  You can mark the path "#sub" as external to exclude it from the bundle, which will remove this
  error. You can also add ".catch()" here to handle this failure at run-time instead of bundle-time.

✘ [ERROR] Could not resolve "subpath-exports/sub"

    <stdin>:1:23:
      1 │ import("#sub"); import("subpath-exports/sub")
        ╵                        ~~~~~~~~~~~~~~~~~~~~~

  The module "./dist/sub.js" was not found on the file system:

    package.json:6:13:
      6 │     "./sub": "./dist/sub.js"
        ╵              ~~~~~~~~~~~~~~~

  You can mark the path "subpath-exports/sub" as external to exclude it from the bundle, which will
  remove this error. You can also add ".catch()" here to handle this failure at run-time instead of
  bundle-time.

2 errors

https://github.com/everett1992/subpath-exports

everett1992 avatar Aug 30 '23 00:08 everett1992

Here's the reproduction: link

Note to self: This request is that esbuild should interpret the rootDir, outDir, module, and moduleResolution settings exactly as TypeScript does in this case. TypeScript does the reverse mapping here to discover the location of the original TypeScript code for these imports.

This feature request is likely pretty complicated to implement because TypeScript's behavior here is likely pretty complex as it involves the combinatorial behavior of a lot of different tsconfig.json settings, or at least it would require a lot of research to implement correctly.

evanw avatar Oct 17 '23 05:10 evanw

Separately of tsconfig, you may want to point subpath exports directly at anticipated output files in a monorepo setting. A workaround for this issue is to add an extra condition (e.g. "source") and pass it to --conditions. Another approach that can work is specifying --external.

samhh avatar Jul 16 '24 14:07 samhh