You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Reduce @sentry/node startup cost by flattening the published build #25680
require('@sentry/node') loads 589 CommonJS modules and takes about 92–100 ms on Node 24.21, measured against the local CJS build. NODE_COMPILE_CACHE brought no measurable gain, so the time goes to resolving, reading and evaluating many small files rather than compiling them. The packages are built with preserved modules, so each source file becomes its own published file. By module count the largest contributors are @sentry/server-utils, @sentry/core, @sentry/server-runtime-injection (its bundled parser and code generator) and @opentelemetry/api.
This cost lands on every cold start (serverless functions, CLIs, autoscaling containers).
Proposal
Reduce the cost through the build output shape. AGENTS.md rules out lazy loading as a fix, so imports stay static:
Publish flattened entry files (instead of preserved modules) for @sentry/node, @sentry/server-utils and @sentry/server-runtime-injection, with the same exports maps and entry points.
Check that nothing which only matters for runtime injection is evaluated on the main entry when it doesn't need to be, using a static restructure rather than lazy require.
Measure before and after with the count-based gate proposed in the perf issue (modules loaded, import time).
Risks to check
Deep imports into build/ paths
@vercel/nft / Next.js file tracing
The chunk layout that server-runtime-injection's hook.js relies on
Dual-package hazards between the CJS and ESM builds
Related: #19885 takes a different approach to the same problem: a separate @sentry/core/light entry for lightweight consumers.
If we include @sentry/core in the set, then we drop the resulting loaded module count down from 269 to 61. (It's 606 now, so it's a big win either way.) A lot of Node consumers resolve core through the require condition, so we could flatten only core's CJS build. The ESM build that browser bundlers and React Native use would stay preserved, which keeps the size-limit and tree-shaking risk away.
we should clarify that each package keeps its @sentry/* deps external. I tried bundling core into @sentry/node, and core loaded twice: once inside the bundle, and once through @sentry/opentelemetry. So there's a dual-instance hazard to watch out for.
This doesn't account for shared chunks. Each package has several entries (core has index, server and browser; server-utils has 10). Without preserved modules, rollup emits shared chunks with hashed names such as trace-kYyA2XQ_.js. We need fixed chunkFileNames, so file names stay stable for nft tracing and for hook.mjs's relative imports.
This should likely happen after the work for JS-3989, I'm assuming? Makes more sense to stack on the guard and other work to prevent code getting where it shouldn't.
Activity
linear-code commentedon Oct 23, 2026
JS-3993
isaacs commentedon Oct 29, 2026
A few thoughts on this:
If we include
@sentry/corein the set, then we drop the resulting loaded module count down from 269 to 61. (It's 606 now, so it's a big win either way.) A lot of Node consumers resolve core through the require condition, so we could flatten only core's CJS build. The ESM build that browser bundlers and React Native use would stay preserved, which keeps the size-limit and tree-shaking risk away.we should clarify that each package keeps its
@sentry/*deps external. I tried bundling core into@sentry/node, and core loaded twice: once inside the bundle, and once through@sentry/opentelemetry. So there's a dual-instance hazard to watch out for.This doesn't account for shared chunks. Each package has several entries (core has index, server and browser; server-utils has 10). Without preserved modules, rollup emits shared chunks with hashed names such as trace-kYyA2XQ_.js. We need fixed
chunkFileNames, so file names stay stable for nft tracing and forhook.mjs's relative imports.This should likely happen after the work for JS-3989, I'm assuming? Makes more sense to stack on the guard and other work to prevent code getting where it shouldn't.
feat(node): Publish flat builds to cut `@sentry/node` startup time
feat(node): Publish flat CJS builds to cut `@sentry/node` startup time