Skip to content

fix(build): only shim node:url/path/module in ESM files that need them#2365

Merged
andybevan-scope3 merged 2 commits into
mainfrom
issue-2364
Jul 17, 2026
Merged

fix(build): only shim node:url/path/module in ESM files that need them#2365
andybevan-scope3 merged 2 commits into
mainfrom
issue-2364

Conversation

@andybevan-scope3

Copy link
Copy Markdown
Collaborator

Summary

  • The tsup ESM banner (node:url/node:path/node:module + __dirname/__filename/require shims) was applied unconditionally via esbuildOptions to every .mjs output — including pure-data files like enums.generated.mjs that never touch those globals.
  • Strict browser bundlers (Vite, esbuild --platform=browser) can't resolve real Node built-ins, so importing @adcp/sdk/enums or other lean entry points broke bundling for browser consumers.
  • Replaced the blanket banner with an esbuild onLoad plugin that only prepends the shim to files whose transpiled body actually references __dirname/__filename/require — measured at 11 of 486 .mjs files. Detection uses a throwaway transformSync (comments/types stripped) rather than raw source matching, so a comment merely mentioning require() can't cause a false positive. Runs pre-transform so esbuild's own sourcemap generation stays accurate for the shimmed files.
  • Added a --platform=browser esbuild bundle check to scripts/verify-package.mjs (already wired into CI for tsup.config.ts changes) so this exact regression fails CI if it recurs.

Fixes #2364.

Test plan

  • Reproduced the reported failure against the built dist/lib/types/enums.generated.mjs with esbuild --bundle --platform=browser before the fix.
  • Confirmed the same repro now bundles cleanly after the fix.
  • Confirmed exactly 11/486 .mjs files retain the shim, matching the files that reference __dirname/__filename/require in their body.
  • node scripts/verify-package.mjs passes (ESM/CJS load + new browser-bundle check).
  • npm run check:package (publint + attw) — all green.
  • npm run typecheck — clean.
  • npm run test:node:fast — same 2 pre-existing failures as on main (unrelated JWK/crypto signing tests), confirmed via bisecting with git stash; no new failures introduced.
  • Changeset added (patch).

🤖 Generated with Claude Code

The tsup ESM banner (node:url/node:path/node:module + __dirname/__filename/
require shims) was applied unconditionally via esbuildOptions to every
.mjs output, including pure-data files like enums.generated.mjs that never
touch those globals. Strict browser bundlers (Vite, esbuild
--platform=browser) can't resolve real Node built-ins, so importing
@adcp/sdk/enums or other lean entry points broke bundling for browser
consumers.

Replace the blanket banner with an esbuild onLoad plugin that only
prepends the shim to files whose transpiled body actually references
__dirname/__filename/require (11 of 486 files) — detected via a
throwaway transformSync so comments merely mentioning "require()" don't
false-positive. Runs pre-transform so sourcemaps stay accurate.

Add a browser-platform esbuild bundle check to verify-package.mjs so this
class of regression fails CI going forward.

Fixes #2364
Two follow-ups from review:
- require(...features) in SingleAgentClient.ts is a class method, not a
  call to the global require — the detection regex matched it anyway,
  giving that file the shim unnecessarily. Every real require() call in
  the codebase takes a string-literal module specifier, so require the
  match to have a following quote.
- __filename is never read by any source file; it only existed as an
  intermediate step toward __dirname. Compute __dirname inline instead of
  binding an unused name.
@andybevan-scope3
andybevan-scope3 merged commit bdfb075 into main Jul 17, 2026
32 checks passed
@andybevan-scope3
andybevan-scope3 deleted the issue-2364 branch July 17, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ESM banner in tsup build unconditionally injects Node built-ins, breaking browser bundlers

2 participants