Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2464302
chore: switch package builds to rolldown
logaretm May 28, 2026
9cad751
fix: deprecation
logaretm Aug 25, 2026
b7d7ce9
fix(aws-serverless): don't build the lambda layer during build:transpile
logaretm Aug 25, 2026
1287407
fix(server-utils): keep the orchestrion transformer tree-shakeable
logaretm Aug 25, 2026
5672fa9
test(node-integration-tests): assert init() survives tree-shaking whe…
logaretm Aug 25, 2026
63a1664
feat(build): restore property mangling in bundle minification
logaretm Aug 26, 2026
cf2d56e
build(feedback): preserve modules in the npm build
logaretm Aug 26, 2026
26580a4
fix(nextjs): restore the ts-expect-error on the config import template
logaretm Aug 26, 2026
641ac58
Revert "build(feedback): preserve modules in the npm build"
logaretm Aug 26, 2026
1db8abc
fix(build): keep process.env.NODE_ENV out of rolldown's define
logaretm Aug 27, 2026
04bc7f9
fix(aws-serverless): resolve the handler shim without a bare require …
logaretm Aug 27, 2026
e817eb6
build(server-runtime-injection): switch to rolldown
logaretm Sep 23, 2026
3b79d0f
chore(deps): Bump rolldown to 1.2.10
logaretm Sep 23, 2026
5aca514
fix(server-runtime-injection): keep the vendored transformer droppabl…
logaretm Sep 23, 2026
3753774
fix(effect): read ErrorReporter with Reflect.get
logaretm Sep 23, 2026
dae57cd
test: adapt tests to rolldown output
logaretm Sep 23, 2026
833f530
test(e2e): Bump rolldown to 1.2.10 in node-rolldown
logaretm Sep 23, 2026
744e528
chore: Bump size limits for rolldown output
logaretm Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,15 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.feedback.min.js'),
gzip: false,
brotli: false,
limit: '291 KB',
limit: '294 KB',
disablePlugins: ['@size-limit/esbuild'],
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed',
path: createCDNPath('bundle.tracing.replay.feedback.logs.metrics.min.js'),
gzip: false,
brotli: false,
limit: '297 KB',
limit: '300 KB',
disablePlugins: ['@size-limit/esbuild'],
},
// Next.js SDK (ESM)
Expand Down Expand Up @@ -452,7 +452,7 @@ module.exports = [
path: 'packages/node/build/esm/index.js',
import: createImport('init'),
gzip: true,
limit: '114 KB',
limit: '115 KB',
disablePlugins: ['@size-limit/esbuild'],
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
modifyWebpackConfig: function (config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ sentryTest('should not add source context lines to errors from script files', as

const exception = eventData.exception?.values?.[0];
const frames = exception?.stacktrace?.frames;
expect(frames).toHaveLength(1);
expect(frames?.length).toBeGreaterThanOrEqual(1);
// Verify the subject.bundle.js frame is present
expect(frames?.some(f => f.filename?.includes('subject.bundle.js'))).toBe(true);
// Core assertion: no context lines should be added for script files
frames?.forEach(f => {
expect(f).not.toHaveProperty('pre_context');
expect(f).not.toHaveProperty('context_line');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ sentryTest('should provide module_metadata on stack frames in beforeSend', async
const url = await getLocalTestUrl({ testDir: __dirname });

const errorEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
expect(errorEvent.extra?.['module_metadata_entries']).toEqual([{ foo: 'bar' }]);
// Filter out null entries from internal Sentry frames that don't have module metadata
const metadataEntries = (errorEvent.extra?.['module_metadata_entries'] as Array<unknown>)?.filter(
entry => entry !== null,
);
expect(metadataEntries).toEqual([{ foo: 'bar' }]);
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ sentryTest(
const url = await getLocalTestUrl({ testDir: __dirname });

const errorEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
expect(errorEvent?.extra?.['module_metadata_entries']).toEqual([{ foo: 'baz' }]);
// Filter out null entries from internal Sentry frames that don't have module metadata
const metadataEntries = (errorEvent?.extra?.['module_metadata_entries'] as Array<unknown>)?.filter(
entry => entry !== null,
);
expect(metadataEntries).toEqual([{ foo: 'baz' }]);
},
);
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
await import('./src/env.js');
require('./src/env.js');

/** @type {import("next").NextConfig} */
const config = {};

import { withSentryConfig } from '@sentry/nextjs/config';
const { withSentryConfig } = require('@sentry/nextjs/config');

export default withSentryConfig(config, {
module.exports = withSentryConfig(config, {
webpack: {
treeshake: {
removeDebugLogging: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "t3",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "next build",
"clean": "npx rimraf node_modules pnpm-lock.yaml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function readOrchestrionPluginGraphSources(): string[] {
const entrySource = fs.readFileSync(pluginEntry, 'utf8');
return [
entrySource,
...[...entrySource.matchAll(/require\('(\.\.?\/[^']+)'\)/g)].map(([, specifier]) =>
...[...entrySource.matchAll(/require\(['"](\.\.?\/[^'"]+)['"]\)/g)].map(([, specifier]) =>
fs.readFileSync(path.resolve(path.dirname(pluginEntry), specifier), 'utf8'),
),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/** @type {import("next").NextConfig} */
const config = {};

import { withSentryConfig } from '@sentry/nextjs/config';
const { withSentryConfig } = require('@sentry/nextjs/config');

export default withSentryConfig(config, {
module.exports = withSentryConfig(config, {
webpack: {
treeshake: {
removeDebugLogging: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "next-orpc",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "next build",
"dev": "next dev -p 3030",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"devDependencies": {
"@sentry-internal/test-utils": "link:../../../test-utils",
"graphql": "16.9.0",
"rolldown": "1.2.5"
"rolldown": "1.2.10"
},
"volta": {
"extends": "../../package.json"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ export default defineConfig({
}),
sveltekit(),
],
build: {
rollupOptions: {
external: ['fsevents'],
},
},
});
2 changes: 1 addition & 1 deletion dev-packages/node-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"scripts": {
"build": "run-s build:transpile build:types",
"build:dev": "yarn build",
"build:transpile": "rollup -c rollup.npm.config.mjs",
"build:transpile": "rolldown -c rollup.npm.config.mjs",
"build:types": "tsc -p tsconfig.types.json",
"clean": "rimraf -g suites/**/node_modules suites/**/tmp_* && run-p clean:script",
"clean:script": "node scripts/clean.js",
Expand Down
14 changes: 14 additions & 0 deletions dev-packages/node-integration-tests/suites/esbuild/app-init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/node';

// `Sentry.init()` synchronously calls `registerDiagnosticsChannelInjection()`, which reaches the
// vendored orchestrion transformer. That chain is split so a bundler can tree-shake it (see
// `makeCjsExportsSplitPlugin` in server-utils' rollup config); this asserts the split degrades to
// "no channel injection" rather than throwing when the bundler drops it.
Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
tracesSampleRate: 0,
autoSessionTracking: false,
});

// eslint-disable-next-line no-console
console.log(`SENTRY_NODE_INITIALIZED client=${Boolean(Sentry.getClient())}`);
30 changes: 30 additions & 0 deletions dev-packages/node-integration-tests/suites/esbuild/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,34 @@ describe('esbuild bundling', () => {
rmSync(outDir, { recursive: true, force: true });
}
});

test('@sentry/node survives init() when bundled to ESM with tree-shaking', async () => {
const outDir = mkdtempSync(join(tmpdir(), 'sentry-esbuild-esm-'));
const outfile = join(outDir, 'bundle.mjs');

try {
await build({
entryPoints: [join(__dirname, 'app-init.ts')],
outfile,
platform: 'node',
format: 'esm',
bundle: true,
// `@sentry/server-utils` is `sideEffects: false`, so this is where a bundler is free to
// drop the vendored orchestrion transformer.
treeShaking: true,
logLevel: 'silent',
});

const result = spawnSync('node', [outfile], { encoding: 'utf-8' });

// Dropping the transformer must not take `init()` with it: reading a tree-shaken CJS
// container has to yield `undefined`, not throw on destructuring or on a call.
expect(result.stderr).not.toContain('Cannot destructure');
expect(result.stderr).not.toContain('is not a function');
expect(result.status).toBe(0);
expect(result.stdout).toContain('SENTRY_NODE_INITIALIZED client=true');
} finally {
rmSync(outDir, { recursive: true, force: true });
}
});
});
65 changes: 36 additions & 29 deletions dev-packages/rollup-utils/bundleHelpers.mjs
Original file line number Diff line number Diff line change
@@ -1,56 +1,50 @@
/**
* Rollup config docs: https://rollupjs.org/guide/en/#big-list-of-options
* Rolldown config docs: https://rolldown.rs/reference/config-options
*/

import { builtinModules } from 'module';
import * as fs from 'fs';
import * as path from 'path';

import deepMerge from 'deepmerge';

import {
makeBannerOptions,
makeBrowserBuildPlugin,
makeCommonJSPlugin,
makeEsbuildPlugin,
makeIsDebugBuildPlugin,
makeLicensePlugin,
makeNodeResolvePlugin,
makeMinifierOptions,
makeRrwebBuildPlugin,
makeSetSDKSourcePlugin,
makeTerserPlugin,
} from './plugins/index.mjs';
import { mergePlugins } from './utils.mjs';
import { getNodeBuiltIns, mergePlugins, treeShakePreset } from './utils.mjs';
import { makeProductionReplacePlugin } from './plugins/npmPlugins.mjs';

const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js'];

const packageDotJSON = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), './package.json'), { encoding: 'utf8' }));

export function makeBaseBundleConfig(options) {
const { bundleType, entrypoints, licenseTitle, outputFileBase, packageSpecificConfig, esbuild } = options;
const { bundleType, entrypoints, licenseTitle, outputFileBase, packageSpecificConfig } = options;

const nodeResolvePlugin = makeNodeResolvePlugin();
const transpilePlugin = makeEsbuildPlugin(esbuild);
const markAsBrowserBuildPlugin = makeBrowserBuildPlugin(true);
const licensePlugin = makeLicensePlugin(licenseTitle);
const banner = makeBannerOptions(licenseTitle, packageDotJSON.version);
const rrwebBuildPlugin = makeRrwebBuildPlugin({
excludeIframe: false,
excludeShadowDom: false,
});
const productionReplacePlugin = makeProductionReplacePlugin();

// The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it
// will include all dependencies, imported or required, in the final bundle. (Without it, CJS modules aren't included
// at all, and without `transformMixedEsModules`, they're only included if they're imported, not if they're required.)
const commonJSPlugin = makeCommonJSPlugin({ transformMixedEsModules: true });

// used by `@sentry/browser`
const standAloneBundleConfig = {
output: {
banner,
format: 'iife',
name: 'Sentry',
intro: () => {
return 'exports = window.Sentry || {};';
},
},
context: 'window',
plugins: [rrwebBuildPlugin, markAsBrowserBuildPlugin, licensePlugin],
plugins: [rrwebBuildPlugin, markAsBrowserBuildPlugin],
};

// used by `@sentry/wasm` & pluggable integrations from core/browser (bundles which need to be combined with a stand-alone SDK bundle)
Expand All @@ -61,7 +55,7 @@ export function makeBaseBundleConfig(options) {
format: 'cjs',

// code to add before the CJS wrapper
banner: '(function (__window) {',
banner: `${banner}\n(function (__window) {`,

// code to add just inside the CJS wrapper, before any of the wrapped code
intro: 'var exports = {};',
Expand All @@ -84,30 +78,41 @@ export function makeBaseBundleConfig(options) {
// code to add after the CJS wrapper
footer: '}(window));',
},
plugins: [rrwebBuildPlugin, markAsBrowserBuildPlugin, licensePlugin],
plugins: [rrwebBuildPlugin, markAsBrowserBuildPlugin],
};

const workerBundleConfig = {
output: {
banner,
format: 'esm',
minify: makeMinifierOptions(),
},
plugins: [commonJSPlugin, makeTerserPlugin(), licensePlugin],
// Don't bundle any of Node's core modules
external: builtinModules,
external: getNodeBuiltIns(),
};

const awsLambdaExtensionBundleConfig = {
output: {
format: 'esm',
minify: makeMinifierOptions(),
},
plugins: [commonJSPlugin, makeIsDebugBuildPlugin(true), makeTerserPlugin()],
plugins: [makeIsDebugBuildPlugin(true)],
// Don't bundle any of Node's core modules
external: builtinModules,
external: getNodeBuiltIns(),
};

// used by all bundles
const sharedBundleConfig = {
input: entrypoints,

// Point at the package's tsconfig so rolldown picks up its TypeScript & JSX settings.
tsconfig: path.resolve(process.cwd(), './tsconfig.json'),

// ES2020 is our floor: keeps `?.`/`??` native and downlevels everything newer.
transform: {
target: 'es2020',
},

output: {
// a file extension will be added to this base value when we specify either a minified or non-minified build
entryFileNames: outputFileBase,
Expand All @@ -116,8 +121,8 @@ export function makeBaseBundleConfig(options) {
strict: false,
esModule: false,
},
plugins: [productionReplacePlugin, transpilePlugin, nodeResolvePlugin],
treeshake: 'smallest',
plugins: [productionReplacePlugin],
treeshake: treeShakePreset('smallest'),
};

const bundleTypeConfigMap = {
Expand Down Expand Up @@ -147,7 +152,7 @@ export function makeBundleConfigVariants(baseConfig, options = {}) {

const includeDebuggingPlugin = makeIsDebugBuildPlugin(true);
const stripDebuggingPlugin = makeIsDebugBuildPlugin(false);
const terserPlugin = makeTerserPlugin();
const minify = makeMinifierOptions();
const setSdkSourcePlugin = makeSetSDKSourcePlugin('cdn');

// The additional options to use for each variant we're going to create.
Expand All @@ -162,15 +167,17 @@ export function makeBundleConfigVariants(baseConfig, options = {}) {
'.min.js': {
output: {
entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.min.js`,
minify,
},
plugins: [stripDebuggingPlugin, setSdkSourcePlugin, terserPlugin],
plugins: [stripDebuggingPlugin, setSdkSourcePlugin],
},

'.debug.min.js': {
output: {
entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.debug.min.js`,
minify,
},
plugins: [includeDebuggingPlugin, setSdkSourcePlugin, terserPlugin],
plugins: [includeDebuggingPlugin, setSdkSourcePlugin],
},
};

Expand Down
2 changes: 1 addition & 1 deletion dev-packages/rollup-utils/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export { plugins };

export * from './bundleHelpers.mjs';
export * from './npmHelpers.mjs';
export { insertAt } from './utils.mjs';
export { insertAt, treeShakePreset } from './utils.mjs';
Loading
Loading