Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
682ce08
chore(build): Use Rolldown
timfish Oct 31, 2025
65fa92e
Lint
timfish Oct 31, 2025
92c7370
fixes
timfish Oct 31, 2025
97645a5
chore: it builds :O
logaretm Nov 11, 2025
3916725
fix: bring back preserve modules
logaretm Nov 11, 2025
2478572
fix: lint
logaretm Nov 11, 2025
14e37e9
chore: remove ts and terser from replay
logaretm Nov 11, 2025
da38566
chore: remove last uses of rollup replace
logaretm Nov 11, 2025
09e01b5
chore: fix replace plugin usage
logaretm Nov 11, 2025
7d06305
chore: remove unused rollup plugins
logaretm Nov 11, 2025
a6883ef
chore: use defineConfig for type assist
logaretm Nov 11, 2025
71c9a22
chore: module preservation root
logaretm Nov 11, 2025
8683c7c
chore: revert some changes
logaretm Nov 11, 2025
418a8fb
fix: revert more changes
logaretm Nov 11, 2025
65887b2
fix: format
logaretm Nov 11, 2025
f050617
fix: nextjs build
logaretm Nov 11, 2025
a55037c
chore: upgrade to rolldown 1.0.0-beta.50
logaretm Nov 13, 2025
09a39bf
chore: properly replace the smallest preset value
logaretm Nov 13, 2025
cfedd61
chore: remove sucrase, and try out latest preview build
logaretm Nov 17, 2025
541ec44
chore: revert to latest beta
logaretm Nov 17, 2025
0530b19
chore: try out latest rolldown preview
logaretm Nov 17, 2025
d78accc
chore: ensure sub files in externals are treated as externals
logaretm Nov 17, 2025
a56e6e7
chore: fix astro export for middleware
logaretm Nov 17, 2025
8fba71f
chore: nextjs config
logaretm Nov 17, 2025
ccb4a8c
chore: fix nextjs output
logaretm Nov 18, 2025
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
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-core-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 **/node_modules && run-p clean:script",
"clean:script": "node scripts/clean.js",
Expand Down
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
43 changes: 17 additions & 26 deletions dev-packages/rollup-utils/bundleHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,46 @@
*/

import { builtinModules } from 'module';

import path from 'node:path';
import fs from 'node:fs';
import deepMerge from 'deepmerge';

import {
makeBrowserBuildPlugin,
makeCleanupPlugin,
makeCommonJSPlugin,
makeIsDebugBuildPlugin,
makeLicensePlugin,
makeNodeResolvePlugin,
makeRrwebBuildPlugin,
makeSetSDKSourcePlugin,
makeSucrasePlugin,
makeBannerOptions,
makeTerserPlugin,
} from './plugins/index.mjs';
import { mergePlugins } from './utils.mjs';
import { makeProductionReplacePlugin } from './plugins/npmPlugins.mjs';
import { mergePlugins, treeShakePreset } from './utils.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, sucrase } = options;
const { bundleType, entrypoints, licenseTitle, outputFileBase, packageSpecificConfig } = options;

const nodeResolvePlugin = makeNodeResolvePlugin();
const sucrasePlugin = makeSucrasePlugin({}, sucrase);
const cleanupPlugin = makeCleanupPlugin();
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 @@ -63,7 +53,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 @@ -86,14 +76,15 @@ 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',
},
plugins: [commonJSPlugin, makeTerserPlugin(), licensePlugin],
plugins: [makeTerserPlugin()],
// Don't bundle any of Node's core modules
external: builtinModules,
};
Expand All @@ -102,7 +93,7 @@ export function makeBaseBundleConfig(options) {
output: {
format: 'esm',
},
plugins: [commonJSPlugin, makeIsDebugBuildPlugin(true), makeTerserPlugin()],
plugins: [makeIsDebugBuildPlugin(true), makeTerserPlugin()],
// Don't bundle any of Node's core modules
external: builtinModules,
};
Expand All @@ -118,8 +109,7 @@ export function makeBaseBundleConfig(options) {
strict: false,
esModule: false,
},
plugins: [productionReplacePlugin, sucrasePlugin, nodeResolvePlugin, cleanupPlugin],
treeshake: 'smallest',
treeshake: treeShakePreset('smallest'),
};

const bundleTypeConfigMap = {
Expand Down Expand Up @@ -180,6 +170,7 @@ export function makeBundleConfigVariants(baseConfig, options = {}) {
if (!BUNDLE_VARIANTS.includes(variant)) {
throw new Error(`Unknown bundle variant requested: ${variant}`);
}

return deepMerge(baseConfig, variantSpecificConfigMap[variant], {
// Merge the plugin arrays and make sure the end result is in the correct order. Everything else can use the
// default merge strategy.
Expand Down
7 changes: 2 additions & 5 deletions dev-packages/rollup-utils/index.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
// TODO Is this necessary?
import * as plugins from './plugins/index.mjs';
export { plugins };

export * from './plugins/index.mjs';
export * from './bundleHelpers.mjs';
export * from './npmHelpers.mjs';
export { insertAt } from './utils.mjs';
export { insertAt, treeShakePreset } from './utils.mjs';
43 changes: 15 additions & 28 deletions dev-packages/rollup-utils/npmHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@ import * as fs from 'fs';
import { builtinModules } from 'module';
import * as path from 'path';
import { fileURLToPath } from 'url';

import deepMerge from 'deepmerge';

import { defineConfig } from 'rollup';
import { defineConfig } from 'rolldown';
import {
makeCleanupPlugin,
makeDebugBuildStatementReplacePlugin,
makeNodeResolvePlugin,
makeProductionReplacePlugin,
makeRrwebBuildPlugin,
makeSucrasePlugin,
} from './plugins/index.mjs';
import { makePackageNodeEsm } from './plugins/make-esm-plugin.mjs';
import { mergePlugins } from './utils.mjs';
Expand All @@ -34,20 +29,23 @@ export function makeBaseNPMConfig(options = {}) {
entrypoints = ['src/index.ts'],
hasBundles = false,
packageSpecificConfig = {},
sucrase = {},
bundledBuiltins = [],
} = options;

const nodeResolvePlugin = makeNodeResolvePlugin();
const sucrasePlugin = makeSucrasePlugin({}, sucrase);
// Make sure subpath imports are also treated as external (e.g., 'solid-js/web' when 'solid-js' is external)
const externalWithSubpaths = [
...Object.keys(packageDotJSON.dependencies || {}),
...Object.keys(packageDotJSON.peerDependencies || {}),
...Object.keys(packageDotJSON.optionalDependencies || {}),
].map(dep => new RegExp(`^${dep}(?:/.*)?$`));

const debugBuildStatementReplacePlugin = makeDebugBuildStatementReplacePlugin();
const cleanupPlugin = makeCleanupPlugin();
const rrwebBuildPlugin = makeRrwebBuildPlugin({
excludeShadowDom: undefined,
excludeIframe: undefined,
});

const defaultBaseConfig = {
const defaultBaseConfig = defineConfig({
input: entrypoints,

output: {
Expand All @@ -62,14 +60,14 @@ export function makeBaseNPMConfig(options = {}) {
// output individual files rather than one big bundle
preserveModules: true,

// output files relative to the src directory
preserveModulesRoot: 'src',

// Allow wrappers or helper functions generated by rollup to use any ES2015 features
generatedCode: {
preset: 'es2015',
},

// don't add `"use strict"` to the top of cjs files
strict: false,

// do TS-3.8-style exports
// exports.dogs = are.great
// rather than TS-3.9-style exports
Expand All @@ -78,12 +76,6 @@ export function makeBaseNPMConfig(options = {}) {
// get: () => are.great,
// });
externalLiveBindings: false,

// Don't call `Object.freeze` on the results of `import * as someModule from '...'`
// (We don't need it, so why waste the bytes?)
freeze: false,

interop: 'esModule',
},

treeshake: {
Expand All @@ -97,16 +89,11 @@ export function makeBaseNPMConfig(options = {}) {
},
},

plugins: [nodeResolvePlugin, sucrasePlugin, debugBuildStatementReplacePlugin, rrwebBuildPlugin, cleanupPlugin],
plugins: [debugBuildStatementReplacePlugin, rrwebBuildPlugin],

// don't include imported modules from outside the package in the final output
external: [
...builtinModules.filter(m => !bundledBuiltins.includes(m)),
...Object.keys(packageDotJSON.dependencies || {}),
...Object.keys(packageDotJSON.peerDependencies || {}),
...Object.keys(packageDotJSON.optionalDependencies || {}),
],
};
external: [...builtinModules.filter(m => !bundledBuiltins.includes(m)), ...externalWithSubpaths],
});

return deepMerge(defaultBaseConfig, packageSpecificConfig, {
// Plugins have to be in the correct order or everything breaks, so when merging we have to manually re-order them
Expand Down
78 changes: 31 additions & 47 deletions dev-packages/rollup-utils/plugins/bundlePlugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,64 +10,55 @@

import * as childProcess from 'child_process';

import commonjs from '@rollup/plugin-commonjs';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { replacePlugin } from 'rolldown/plugins';
import terser from '@rollup/plugin-terser';
import license from 'rollup-plugin-license';

/**
* Create a plugin to add an identification banner to the top of stand-alone bundles.
*
* @param title The title to use for the SDK, if not the package name
* @returns An instance of the `rollup-plugin-license` plugin
* @param version The version of the SDK
*/
export function makeLicensePlugin(title) {
export function makeBannerOptions(title, version) {
const commitHash = childProcess.execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim();

const plugin = license({
banner: {
content: `/*! <%= data.title %> <%= pkg.version %> (${commitHash}) | https://github.com/getsentry/sentry-javascript */`,
data: { title },
},
});

// give it a nicer name for later, when we'll need to sort the plugins
plugin.name = 'license';

return plugin;
return `/*! ${title} ${version} (${commitHash}) | https://github.com/getsentry/sentry-javascript */`;
}

/**
* Create a plugin to set the value of the `__SENTRY_DEBUG__` magic string.
*
* @param includeDebugging Whether or not the resulting build should include log statements
* @returns An instance of the `@rollup/plugin-replace` plugin to do the replacement of the magic string with `true` or
* @returns An instance of the `rolldown.replacePlugin` plugin to do the replacement of the magic string with `true` or
* 'false`
*/
export function makeIsDebugBuildPlugin(includeDebugging) {
return replace({
// TODO `preventAssignment` will default to true in version 5.x of the replace plugin, at which point we can get rid
// of this. (It actually makes no difference in this case whether it's true or false, since we never assign to
// `__SENTRY_DEBUG__`, but if we don't give it a value, it will spam with warnings.)
preventAssignment: true,
values: {
return replacePlugin(
{
// Flags in current package
__DEBUG_BUILD__: includeDebugging,
__DEBUG_BUILD__: JSON.stringify(includeDebugging),
// Flags in built monorepo dependencies, from which the bundle pulls
__SENTRY_DEBUG__: includeDebugging,
__SENTRY_DEBUG__: JSON.stringify(includeDebugging),
},
});
{
// TODO `preventAssignment` will default to true in version 5.x of the replace plugin, at which point we can get rid
// of this. (It actually makes no difference in this case whether it's true or false, since we never assign to
// `__SENTRY_DEBUG__`, but if we don't give it a value, it will spam with warnings.)
preventAssignment: true,
},
);
}

export function makeSetSDKSourcePlugin(sdkSource) {
return replace({
preventAssignment: false,
delimiters: ['', ''],
values: {
return replacePlugin(
{
'/* __SENTRY_SDK_SOURCE__ */': `return ${JSON.stringify(sdkSource)};`,
},
});
{
preventAssignment: false,
delimiters: ['', ''],
},
);
}

/**
Expand All @@ -77,13 +68,15 @@ export function makeSetSDKSourcePlugin(sdkSource) {
* @returns An instance of the `replace` plugin to do the replacement of the magic string with `true` or 'false`
*/
export function makeBrowserBuildPlugin(isBrowserBuild) {
return replace({
// TODO This will be the default in the next version of the `replace` plugin
preventAssignment: true,
values: {
__SENTRY_BROWSER_BUNDLE__: isBrowserBuild,
return replacePlugin(
{
__SENTRY_BROWSER_BUNDLE__: JSON.stringify(!!isBrowserBuild),
},
});
{
// TODO This will be the default in the next version of the `replace` plugin
preventAssignment: true,
},
);
}

// `terser` options reference: https://github.com/terser/terser#api-reference
Expand Down Expand Up @@ -146,12 +139,3 @@ export function makeTerserPlugin() {
},
});
}

// We don't pass these plugins any options which need to be calculated or changed by us, so no need to wrap them in
// another factory function, as they are themselves already factory functions.

export function makeNodeResolvePlugin() {
return nodeResolve();
}

export { commonjs as makeCommonJSPlugin };
7 changes: 4 additions & 3 deletions dev-packages/rollup-utils/plugins/make-esm-plugin.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'node:fs';
import path from 'node:path';

/**
* Outputs a package.json file with {type: module} in the root of the output directory so that Node
Expand All @@ -11,9 +12,9 @@ export function makePackageNodeEsm() {
// We need to keep the `sideEffects` value from the original package.json,
// as e.g. webpack seems to depend on this
// without this, tree shaking does not work as expected
const packageJSONPath = (await this.resolve('package.json')).id;

const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf-8'));
const packageJSON = JSON.parse(
fs.readFileSync(path.resolve(process.cwd(), './package.json'), { encoding: 'utf8' }),
);
const sideEffects = packageJSON.sideEffects;
// For module federation we need to keep the version of the package
const version = packageJSON.version;
Expand Down
Loading
Loading