Skip to content

fix(build): Make CJS re-exports visible to Node ESM imports - #25599

Open
s1gr1d wants to merge 1 commit into
developfrom
sigridhuemer/js-3215-sentrynextjs-node-export-condition-is-a-bare-cjs-path-with
Open

s1gr1d wants to merge 1 commit into
developfrom
sigridhuemer/js-3215-sentrynextjs-node-export-condition-is-a-bare-cjs-path-with

Conversation

@s1gr1d

@s1gr1d s1gr1d commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

When plain Node ESM code imports @sentry/nextjs, Node loads the CJS build, because the node export condition points to it. Namespace imports then miss about 180 exports (captureException, setUser, …), which are silently undefined.

Rollup writes export * in CJS as a loop with an arrow function, but Node only recognizes that loop with a function callback. So Node can't see the @sentry/node re-exports. Fixed by turning off arrow functions in Rollup's generated helpers.

// before
Object.keys(node).forEach(k => { if (k !== 'default' && ...) exports[k] = node[k]; });

// after
Object.keys(browser).forEach(function (k) { /* ... */ }

Splitting the node condition is not needed for this fix. It is planned for later: #25598

  • Not breaking: this only makes hidden exports visible. require() and default imports work as before
  • Only that loop changes, and only in CJS output. ESM output and CDN bundles are unchanged (same bundle-size)
  • Also fixes @sentry/remix, which pins node to CJS too
  • Supersedes fix(nextjs): Expose common server APIs to Node ESM imports #25378, which re-exports only 8 of the missing names

A unit test checks that Node sees the @sentry/node re-export.

Fixes #22791

When plain Node ESM code imports `@sentry/nextjs`, Node loads the CJS build, because the `node` export condition points to it. Namespace imports then miss about 180 exports (`captureException`, `setUser`, …), which are silently `undefined`.

Rollup writes `export *` in CJS as a loop with an arrow function, but Node only recognizes that loop with a `function` callback. So Node can't see the `@sentry/node` re-exports. Fixed by turning off arrow functions in Rollup's generated helpers.

- Only that loop changes, and only in CJS output. ESM output and CDN bundles are unchanged, so size-limit is not affected.
- Also fixes `@sentry/remix`, which pins `node` to CJS too.
- On Node 20, 22 and 24, namespace imports now match `require()`. Bun and Deno were not affected.
- Splitting the `node` condition is not needed for this fix and is still blocked (see #23628).
- Supersedes #25378, which re-exports only 8 of the missing names.

A unit test checks that Node sees the `@sentry/node` re-export.

Fixes #22791
@s1gr1d
s1gr1d requested a review from a team as a code owner October 21, 2026 09:09
@s1gr1d
s1gr1d requested review from chargome and nicohrubec and removed request for a team October 21, 2026 09:09
@linear-code

linear-code Bot commented Oct 21, 2026

Copy link
Copy Markdown

JS-3215

@github-actions

Copy link
Copy Markdown
Contributor

size-limit report 📦

Compared: 541d164 (PR test merge using develop at a01cfca)

Against: a01cfca (develop) · base run #62876

✅ Baseline matches: this diff contains only this PR's changes.

Path Size % Change Change
@sentry/browser 29.91 kB - -
@sentry/browser - with treeshaking flags 28.04 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.95 kB - -
@sentry/browser (incl. Tracing) 52.1 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 52.1 kB - -
@sentry/browser (incl. Tracing, Profiling) 55.04 kB - -
@sentry/browser (incl. Tracing, Replay) 91.85 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 80.72 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 96.58 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 109.52 kB - -
@sentry/browser (incl. Feedback) 47.42 kB - -
@sentry/browser (incl. sendFeedback) 34.97 kB - -
@sentry/browser (incl. FeedbackAsync) 40.07 kB - -
@sentry/browser (incl. Metrics) 30.93 kB - -
@sentry/browser (incl. Logs) 31.21 kB - -
@sentry/browser (incl. Metrics & Logs) 31.84 kB - -
@sentry/react 31.73 kB - -
@sentry/react (incl. Tracing) 54.41 kB - -
@sentry/vue 38.06 kB - -
@sentry/vue (incl. Tracing) 55.03 kB - -
@sentry/svelte 29.93 kB - -
@sentry/remix (Remix 3 client bundle) 57.11 kB - -
CDN Bundle 31.67 kB - -
CDN Bundle (incl. Tracing) 52.61 kB - -
CDN Bundle (incl. Logs, Metrics) 33.87 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 54.55 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.79 kB - -
CDN Bundle (incl. Tracing, Replay) 90.28 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 92.21 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 96.42 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.37 kB - -
CDN Bundle - uncompressed 93.29 kB - -
CDN Bundle (incl. Tracing) - uncompressed 156.14 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.83 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 162.1 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.87 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 276.31 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 282.25 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 290.01 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 295.94 kB - -
@sentry/nextjs (client) 57.3 kB - -
@sentry/sveltekit (client) 52.48 kB - -
@sentry/core/server 41.8 kB - -
@sentry/core/browser 13.81 kB - -
@sentry/node 155.31 kB +0.01% +1 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 84.06 kB - -
@sentry/node - without tracing 96.95 kB - -
@sentry/node - without channel injection 133.29 kB - -
@sentry/aws-serverless 105.13 kB - -
@sentry/cloudflare (withSentry) - minified 211.11 kB - -
@sentry/cloudflare (withSentry) 523.83 kB - -
@sentry/nextjs/cloudflare (withSentry) - minified 230.74 kB - -

View base workflow run

@s1gr1d

s1gr1d commented Oct 21, 2026

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3768aa9. Configure here.

const { reexports } = parse(readFileSync(resolve(__dirname, '../build/cjs/index.server.js'), 'utf8'));

expect(reexports).toContain('@sentry/node');
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test asserts the wrong re-export

Medium Severity

The new regression test expects cjs-module-lexer to report @sentry/node on index.server.js, but that entry only star-exports the local ./server module. The @sentry/node star-export lives in server/index.js. The assertion can fail even after the helper change, so this does not lock in the Node ESM interop fix.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 3768aa9. Configure here.

This branch has not been deployed

No deployments
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.

@sentry/nextjs: "node" export condition is a bare CJS path with no import/require split, so ESM consumers get a partial namespace

2 participants