esbuild
esbuild copied to clipboard
esbuild improperly follows tsconfig.json paths, causing duplicated imports
When running esbuild in --bundle
mode it seems to apply the tsconfig.json paths
mapping before looking at package.json. This causes a condition where both the compiled JS output and original TS source are included in the final bundle. I believe that this is a violation of node's package specification: https://nodejs.org/api/packages.html#self-referencing-a-package-using-its-name
I've put together a very simple example of the issue at https://github.com/laverdet/esbuild-issue.
-> % git clone [email protected]:laverdet/esbuild-issue.git
[...]
-> % cd esbuild-issue
-> % npm run -s run
# node main.js
dist dist
-> % npm run -s build
# npx esbuild --bundle main.js | node --input-type module
dist source
We can see the execution result of the bundled esbuild output disagrees with nodejs's output. This causes very difficult to troubleshoot variation of the dual package hazard.
I discovered a workaround for this issue by passing --resolve-extensions=.js
.
Is there a reason why that package is publishing a tsconfig.json
?
It's an internal package which is set up in a pnpm monorepo. TypeScript has emit
enabled so that we can run tools which depend on this package via the nodejs cli. We also use esbuild to create bundles for deployment.
I also find this surprising:
-> % cat entry.js
import wow from "wow";
console.log(wow);
-> % cat wow.js
export default "wow";
-> % cat tsconfig.json
{
"compilerOptions": {
"paths": { "wow": [ "./wow.js" ] }
}
}
-> % npx esbuild --bundle entry.js
(() => {
// wow.js
var wow_default = "wow";
// entry.js
console.log(wow_default);
})();
I think that this should fail to build since the TypeScript resolution configuration should have no effect on plain .js files. I believe that these two situations are caused by the same underlying issue.