Skip to content

bun export condition points at the ESM build, breaking CJS require() under Bun #350

Description

@jpr5

The package's exports map (both 3.0.7 and current 4.1.0) is:

".": {
  "bun":     "./dist/index.js",
  "deno":    "./dist/index.js",
  "source":  "./src/index.ts",
  "import":  "./dist/index.js",
  "require": "./dist/index.cjs",
  "default": "./dist/index.js"
}

Bun resolves the bun condition before require. Since bun points at the ESM build (dist/index.js), a CommonJS consumer doing require("eventsource") under Bun gets the ESM module instead of the CJS one, and throws:

TypeError: require() async module ".../eventsource/dist/index.js" is unsupported. use "await import()" instead.

dist/index.cjs exists and is a correct CJS build, but the bun condition being checked first means require is never reached — the file is unreachable under Bun regardless of what a consumer does.

This affects both 3.0.7 and 4.1.0.

It also presents intermittently, which makes it easy to dismiss as flaky: this is a load-order race, not a probabilistic bug. If something earlier in the same process has already pulled in eventsource via the ESM graph, Bun's module cache may serve a later require("eventsource") synchronously from that already-resolved instance, and it succeeds. Whether it fails depends on which caller reaches the module first, not on anything random about eventsource itself.

Suggested fixes, either would resolve it:

  • Point the bun condition at ./dist/index.cjs instead of the ESM build, or
  • Drop the bun condition entirely and let Bun fall through to import (for ESM consumers) or require (for CJS consumers), same as Node's resolution.

Separately, I saw #349 is open and removes the CommonJS build entirely. If dist/index.cjs goes away, would a CJS require("eventsource") under Bun then resolve to the ESM build unconditionally? Is the intent that the ESM output no longer be requireable at all under Bun, or is there a reason the top-level-await/async-module shape of the ESM build wouldn't apply here? Just want to understand whether #349 is expected to close this out or make it more consistently reproducible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions