Skip to content

ESM entry module's exports signature is not preserved when required() by other modules #706

Closed
@yyx990803

Description

@yyx990803

Context: I am using esbuild to do dependency-pre-bundling in Vite. This is a multiple-entry build with splitting: true. The entries are resolved package entry files - e.g.

- node_modules/preact/index.module.js
- node_modules/react/index.js

I'm also using a plugin with onResolve that redirects any imports to preact or react to these already resolved file locations to avoid duplicated copies of them in the resulting bundle.

Preact is a pure ESM module, so its corresponding output entry chunk is expected to preserve the same export signature as the original entry module. However, this is broken if any transitive module in the bundle contains a cjs require() call to it. E.g. if somewhere down the tree a dependency does this:

const preact = require('preact')

This causes esbuild to wrap the ESM preact module with a CommonJS wrapper and expose only a default export. This makes sense for the internal interop, but currently this also affects the final output entry chunk for preact: now the output chunk for preact also only has a default export! This creates a mismatch between the output of the preact chunk and the original entry chunk.

What I imagine esbuild needs to do is to

  • move both the preact module and its wrapped CommonJS code to a shared chunk
  • re-export the same original exports from the preact entry chunk
  • import and use the wrapped CommonJS version in the sub dependency that uses require()

Currently I have to work around this by detecting exports mismatch between input/output entry chunks and perform additional runtime interop. Would be great to have this fixed in esbuild itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions