From 790f53f7d83d46513e85db9bc13e246f867cc2ce Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 15 Apr 2026 16:37:17 +0200 Subject: [PATCH 1/5] perf(browser): Parallelize CDN bundle builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a lightweight parallel rollup runner that processes config arrays concurrently using the rollup JS API. Use it for @sentry/browser's build:bundle which runs 93 rollup builds (31 entrypoints × 3 variants). This reduces the browser bundle build from ~175s to ~65s (~2.6x faster), which was the critical path for the full `yarn build`. Co-Authored-By: Claude Opus 4.6 (1M context) --- dev-packages/rollup-utils/rollupParallel.mjs | 34 ++++++++++++++++++++ packages/browser/package.json | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 dev-packages/rollup-utils/rollupParallel.mjs diff --git a/dev-packages/rollup-utils/rollupParallel.mjs b/dev-packages/rollup-utils/rollupParallel.mjs new file mode 100644 index 000000000000..054cf02bace3 --- /dev/null +++ b/dev-packages/rollup-utils/rollupParallel.mjs @@ -0,0 +1,34 @@ +/** + * Runs rollup builds in parallel for configs that export an array. + * Usage: node rollupParallel.mjs + */ +import { cpus } from 'os'; +import { pathToFileURL } from 'url'; +import { resolve } from 'path'; +import { rollup } from 'rollup'; + +const configPath = process.argv[2]; +if (!configPath) { + console.error('Usage: node rollupParallel.mjs '); + process.exit(1); +} + +const { default: configs } = await import(pathToFileURL(resolve(configPath)).href); +const concurrency = cpus().length; +const queue = [...configs]; +let done = 0; + +async function worker() { + while (queue.length > 0) { + const config = queue.shift(); + const bundle = await rollup({ ...config, onwarn: () => {} }); + await bundle.write(config.output); + await bundle.close(); + done++; + process.stdout.write(`\r [${done}/${configs.length}] builds completed`); + } +} + +console.log(`Running ${configs.length} rollup builds (concurrency: ${concurrency})...`); +await Promise.all(Array.from({ length: concurrency }, worker)); +console.log('\nDone.'); diff --git a/packages/browser/package.json b/packages/browser/package.json index 2c98e4aaa343..5d41723e5c6e 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -57,7 +57,7 @@ "scripts": { "build": "run-p build:transpile build:bundle build:types", "build:dev": "run-p build:transpile build:types", - "build:bundle": "rollup -c rollup.bundle.config.mjs", + "build:bundle": "node ../../dev-packages/rollup-utils/rollupParallel.mjs rollup.bundle.config.mjs", "build:transpile": "rollup -c rollup.npm.config.mjs", "build:types": "run-s build:types:core build:types:downlevel", "build:types:core": "tsc -p tsconfig.types.json", From b8fd53bb9e302373be3923116c861a842a4c6f32 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 15 Apr 2026 17:23:24 +0200 Subject: [PATCH 2/5] smalla djuystments --- dev-packages/rollup-utils/rollupParallel.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev-packages/rollup-utils/rollupParallel.mjs b/dev-packages/rollup-utils/rollupParallel.mjs index 054cf02bace3..ce6b76eab043 100644 --- a/dev-packages/rollup-utils/rollupParallel.mjs +++ b/dev-packages/rollup-utils/rollupParallel.mjs @@ -1,3 +1,5 @@ +/* eslint-disable no-console */ + /** * Runs rollup builds in parallel for configs that export an array. * Usage: node rollupParallel.mjs @@ -21,7 +23,7 @@ let done = 0; async function worker() { while (queue.length > 0) { const config = queue.shift(); - const bundle = await rollup({ ...config, onwarn: () => {} }); + const bundle = await rollup({ ...config, onwarn: console.warn }); await bundle.write(config.output); await bundle.close(); done++; From e92bf749242e744fa4ca08a9e8c9f4eaa1e3d758 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Thu, 16 Apr 2026 09:27:31 +0200 Subject: [PATCH 3/5] use availableParallellism --- dev-packages/rollup-utils/rollupParallel.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/rollup-utils/rollupParallel.mjs b/dev-packages/rollup-utils/rollupParallel.mjs index ce6b76eab043..693a179dfb7e 100644 --- a/dev-packages/rollup-utils/rollupParallel.mjs +++ b/dev-packages/rollup-utils/rollupParallel.mjs @@ -4,7 +4,7 @@ * Runs rollup builds in parallel for configs that export an array. * Usage: node rollupParallel.mjs */ -import { cpus } from 'os'; +import { availableParallelism } from 'os'; import { pathToFileURL } from 'url'; import { resolve } from 'path'; import { rollup } from 'rollup'; @@ -16,7 +16,7 @@ if (!configPath) { } const { default: configs } = await import(pathToFileURL(resolve(configPath)).href); -const concurrency = cpus().length; +const concurrency = availableParallelism(); const queue = [...configs]; let done = 0; From ba910f6cb3babb5aa83203467dba286b8ae3bb1e Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Thu, 16 Apr 2026 09:44:33 +0200 Subject: [PATCH 4/5] properly parallelize builds --- .vscode/settings.json | 5 +- dev-packages/rollup-utils/bundleHelpers.mjs | 80 +++++++------ docs/adding-cdn-bundle.md | 6 +- packages/browser/rollup.bundle.config.mjs | 110 +++++++++--------- packages/feedback/rollup.bundle.config.mjs | 4 +- .../replay-canvas/rollup.bundle.config.mjs | 6 +- .../replay-internal/rollup.bundle.config.mjs | 6 +- packages/wasm/rollup.bundle.config.mjs | 6 +- 8 files changed, 122 insertions(+), 101 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 37ff1f20dd2e..4910a772a4c0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,5 +29,8 @@ "[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" }, - "oxc.suppressProgramErrors": true + "oxc.suppressProgramErrors": true, + "[javascript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + } } diff --git a/dev-packages/rollup-utils/bundleHelpers.mjs b/dev-packages/rollup-utils/bundleHelpers.mjs index 8dd2ebd21999..5bd9a5acbbe1 100644 --- a/dev-packages/rollup-utils/bundleHelpers.mjs +++ b/dev-packages/rollup-utils/bundleHelpers.mjs @@ -135,55 +135,69 @@ export function makeBaseBundleConfig(options) { }); } +/** + * @param {import('rollup').RollupOptions} baseConfig + * @param {string} variant + */ +function getVariantSpecificBundleConfig(baseConfig, variant) { + const baseEntryNames = baseConfig.output.entryFileNames; + + switch (variant) { + case '.js': + return { + output: { + entryFileNames: chunkInfo => `${baseEntryNames(chunkInfo)}.js`, + }, + plugins: [makeIsDebugBuildPlugin(true), makeSetSDKSourcePlugin('cdn')], + }; + case '.min.js': + return { + output: { + entryFileNames: chunkInfo => `${baseEntryNames(chunkInfo)}.min.js`, + }, + plugins: [makeIsDebugBuildPlugin(false), makeSetSDKSourcePlugin('cdn'), makeTerserPlugin()], + }; + case '.debug.min.js': + return { + output: { + entryFileNames: chunkInfo => `${baseEntryNames(chunkInfo)}.debug.min.js`, + }, + plugins: [makeIsDebugBuildPlugin(true), makeSetSDKSourcePlugin('cdn'), makeTerserPlugin()], + }; + default: + throw new Error(`Unknown bundle variant requested: ${variant}`); + } +} + /** * Takes the CDN rollup config for a given package and produces three versions of it: * - non-minified, including debug logging, * - minified, including debug logging, * - minified, with debug logging stripped * - * @param baseConfig The rollup config shared by the entire package - * @returns An array of versions of that config + * Pass `() => makeBaseBundleConfig({ ... })` so each variant gets a fresh base config (new plugin instances). That + * avoids sharing stateful Rollup plugins when `rollupParallel` runs multiple `rollup()` calls concurrently. Passing a + * plain config object is supported for backwards compatibility but only shallow-clones plugin shells. + * + * @param {(() => import('rollup').RollupOptions) | import('rollup').RollupOptions} getBaseConfigOrConfig + * @param {{ variants?: string[] }} [options] */ -export function makeBundleConfigVariants(baseConfig, options = {}) { +export function makeBundleConfigVariants(getBaseConfigOrConfig, options = {}) { const { variants = BUNDLE_VARIANTS } = options; - - const includeDebuggingPlugin = makeIsDebugBuildPlugin(true); - const stripDebuggingPlugin = makeIsDebugBuildPlugin(false); - const terserPlugin = makeTerserPlugin(); - const setSdkSourcePlugin = makeSetSDKSourcePlugin('cdn'); - - // The additional options to use for each variant we're going to create. - const variantSpecificConfigMap = { - '.js': { - output: { - entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.js`, - }, - plugins: [includeDebuggingPlugin, setSdkSourcePlugin], - }, - - '.min.js': { - output: { - entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.min.js`, - }, - plugins: [stripDebuggingPlugin, setSdkSourcePlugin, terserPlugin], - }, - - '.debug.min.js': { - output: { - entryFileNames: chunkInfo => `${baseConfig.output.entryFileNames(chunkInfo)}.debug.min.js`, - }, - plugins: [includeDebuggingPlugin, setSdkSourcePlugin, terserPlugin], - }, - }; + const resolveBase = typeof getBaseConfigOrConfig === 'function' ? getBaseConfigOrConfig : () => getBaseConfigOrConfig; return variants.map(variant => { if (!BUNDLE_VARIANTS.includes(variant)) { throw new Error(`Unknown bundle variant requested: ${variant}`); } - return deepMerge(baseConfig, variantSpecificConfigMap[variant], { + const baseConfig = resolveBase(); + const merged = deepMerge(baseConfig, getVariantSpecificBundleConfig(baseConfig, variant), { // Merge the plugin arrays and make sure the end result is in the correct order. Everything else can use the // default merge strategy. customMerge: key => (key === 'plugins' ? mergePlugins : undefined), }); + return { + ...merged, + }; }); } diff --git a/docs/adding-cdn-bundle.md b/docs/adding-cdn-bundle.md index ceb94f575aae..f2b2a0f93268 100644 --- a/docs/adding-cdn-bundle.md +++ b/docs/adding-cdn-bundle.md @@ -130,18 +130,18 @@ describe('index.bundle.{FEATURE_COMBO}', () => { Add bundle config before `builds.push(...)`: ```javascript -const {featureCombo}BaseBundleConfig = makeBaseBundleConfig({ +const {featureCombo}BaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.{FEATURE_COMBO}.ts'], licenseTitle: '@sentry/browser ({Human Readable Feature List})', outputFileBase: () => 'bundles/bundle.{FEATURE_COMBO}', -}); +}; ``` Add to `builds.push(...)`: ```javascript -...makeBundleConfigVariants({featureCombo}BaseBundleConfig), +...makeBundleConfigVariants(() => makeBaseBundleConfig({featureCombo}BaseBundleOptions)), ``` ### 4. `.size-limit.js` diff --git a/packages/browser/rollup.bundle.config.mjs b/packages/browser/rollup.bundle.config.mjs index 2a70d25dac77..0ee09d42c96f 100644 --- a/packages/browser/rollup.bundle.config.mjs +++ b/packages/browser/rollup.bundle.config.mjs @@ -22,31 +22,35 @@ const reexportedPluggableIntegrationFiles = [ ]; browserPluggableIntegrationFiles.forEach(integrationName => { - const integrationsBundleConfig = makeBaseBundleConfig({ - bundleType: 'addon', - entrypoints: [`src/integrations/${integrationName}.ts`], - licenseTitle: `@sentry/browser - ${integrationName}`, - outputFileBase: () => `bundles/${integrationName}`, - }); - - builds.push(...makeBundleConfigVariants(integrationsBundleConfig)); + builds.push( + ...makeBundleConfigVariants(() => + makeBaseBundleConfig({ + bundleType: 'addon', + entrypoints: [`src/integrations/${integrationName}.ts`], + licenseTitle: `@sentry/browser - ${integrationName}`, + outputFileBase: () => `bundles/${integrationName}`, + }), + ), + ); }); reexportedPluggableIntegrationFiles.forEach(integrationName => { - const integrationsBundleConfig = makeBaseBundleConfig({ - bundleType: 'addon', - entrypoints: [`src/integrations-bundle/index.${integrationName}.ts`], - licenseTitle: `@sentry/browser - ${integrationName}`, - outputFileBase: () => `bundles/${integrationName}`, - }); - - builds.push(...makeBundleConfigVariants(integrationsBundleConfig)); + builds.push( + ...makeBundleConfigVariants(() => + makeBaseBundleConfig({ + bundleType: 'addon', + entrypoints: [`src/integrations-bundle/index.${integrationName}.ts`], + licenseTitle: `@sentry/browser - ${integrationName}`, + outputFileBase: () => `bundles/${integrationName}`, + }), + ), + ); }); // Bundle config for additional exports we don't want to include in the main SDK bundle // if we need more of these, we can generalize the config as for pluggable integrations builds.push( - ...makeBundleConfigVariants( + ...makeBundleConfigVariants(() => makeBaseBundleConfig({ bundleType: 'addon', entrypoints: ['src/pluggable-exports-bundle/index.multiplexedtransport.ts'], @@ -56,104 +60,104 @@ builds.push( ), ); -const baseBundleConfig = makeBaseBundleConfig({ +const baseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.ts'], licenseTitle: '@sentry/browser', outputFileBase: () => 'bundles/bundle', -}); +}; -const feedbackBaseBundleConfig = makeBaseBundleConfig({ +const feedbackBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.feedback.ts'], licenseTitle: '@sentry/browser & @sentry/feedback', outputFileBase: () => 'bundles/bundle.feedback', -}); +}; -const logsMetricsBaseBundleConfig = makeBaseBundleConfig({ +const logsMetricsBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.logs.metrics.ts'], licenseTitle: '@sentry/browser (Logs and Metrics)', outputFileBase: () => 'bundles/bundle.logs.metrics', -}); +}; -const replayBaseBundleConfig = makeBaseBundleConfig({ +const replayBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.replay.ts'], licenseTitle: '@sentry/browser (Replay)', outputFileBase: () => 'bundles/bundle.replay', -}); +}; -const replayFeedbackBaseBundleConfig = makeBaseBundleConfig({ +const replayFeedbackBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.replay.feedback.ts'], licenseTitle: '@sentry/browser (Replay, and Feedback)', outputFileBase: () => 'bundles/bundle.replay.feedback', -}); +}; -const replayLogsMetricsBaseBundleConfig = makeBaseBundleConfig({ +const replayLogsMetricsBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.replay.logs.metrics.ts'], licenseTitle: '@sentry/browser (Replay, Logs, and Metrics)', outputFileBase: () => 'bundles/bundle.replay.logs.metrics', -}); +}; // Tracing -const tracingBaseBundleConfig = makeBaseBundleConfig({ +const tracingBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.tracing.ts'], licenseTitle: '@sentry/browser (Performance Monitoring)', outputFileBase: () => 'bundles/bundle.tracing', -}); +}; -const tracingLogsMetricsBaseBundleConfig = makeBaseBundleConfig({ +const tracingLogsMetricsBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.tracing.logs.metrics.ts'], licenseTitle: '@sentry/browser (Performance Monitoring, Logs, and Metrics)', outputFileBase: () => 'bundles/bundle.tracing.logs.metrics', -}); +}; -const tracingReplayBaseBundleConfig = makeBaseBundleConfig({ +const tracingReplayBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.tracing.replay.ts'], licenseTitle: '@sentry/browser (Performance Monitoring and Replay)', outputFileBase: () => 'bundles/bundle.tracing.replay', -}); +}; -const tracingReplayLogsMetricsBaseBundleConfig = makeBaseBundleConfig({ +const tracingReplayLogsMetricsBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.tracing.replay.logs.metrics.ts'], licenseTitle: '@sentry/browser (Performance Monitoring, Replay, Logs, and Metrics)', outputFileBase: () => 'bundles/bundle.tracing.replay.logs.metrics', -}); +}; -const tracingReplayFeedbackBaseBundleConfig = makeBaseBundleConfig({ +const tracingReplayFeedbackBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.tracing.replay.feedback.ts'], licenseTitle: '@sentry/browser (Performance Monitoring, Replay, and Feedback)', outputFileBase: () => 'bundles/bundle.tracing.replay.feedback', -}); +}; -const tracingReplayFeedbackLogsMetricsBaseBundleConfig = makeBaseBundleConfig({ +const tracingReplayFeedbackLogsMetricsBaseBundleOptions = { bundleType: 'standalone', entrypoints: ['src/index.bundle.tracing.replay.feedback.logs.metrics.ts'], licenseTitle: '@sentry/browser (Performance Monitoring, Replay, Feedback, Logs, and Metrics)', outputFileBase: () => 'bundles/bundle.tracing.replay.feedback.logs.metrics', -}); +}; builds.push( - ...makeBundleConfigVariants(baseBundleConfig), - ...makeBundleConfigVariants(feedbackBaseBundleConfig), - ...makeBundleConfigVariants(logsMetricsBaseBundleConfig), - ...makeBundleConfigVariants(replayBaseBundleConfig), - ...makeBundleConfigVariants(replayFeedbackBaseBundleConfig), - ...makeBundleConfigVariants(replayLogsMetricsBaseBundleConfig), - ...makeBundleConfigVariants(tracingBaseBundleConfig), - ...makeBundleConfigVariants(tracingLogsMetricsBaseBundleConfig), - ...makeBundleConfigVariants(tracingReplayBaseBundleConfig), - ...makeBundleConfigVariants(tracingReplayLogsMetricsBaseBundleConfig), - ...makeBundleConfigVariants(tracingReplayFeedbackBaseBundleConfig), - ...makeBundleConfigVariants(tracingReplayFeedbackLogsMetricsBaseBundleConfig), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(baseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(feedbackBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(logsMetricsBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(replayBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(replayFeedbackBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(replayLogsMetricsBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(tracingBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(tracingLogsMetricsBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(tracingReplayBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(tracingReplayLogsMetricsBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(tracingReplayFeedbackBaseBundleOptions)), + ...makeBundleConfigVariants(() => makeBaseBundleConfig(tracingReplayFeedbackLogsMetricsBaseBundleOptions)), ); export default builds; diff --git a/packages/feedback/rollup.bundle.config.mjs b/packages/feedback/rollup.bundle.config.mjs index a5efc6140c06..b3af0af1f45d 100644 --- a/packages/feedback/rollup.bundle.config.mjs +++ b/packages/feedback/rollup.bundle.config.mjs @@ -3,7 +3,7 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal export default [ // The core `feedback` bundle is built in the browser package // Sub-bundles are built here - ...makeBundleConfigVariants( + ...makeBundleConfigVariants(() => makeBaseBundleConfig({ bundleType: 'addon', entrypoints: ['src/screenshot/integration.ts'], @@ -19,7 +19,7 @@ export default [ }, }), ), - ...makeBundleConfigVariants( + ...makeBundleConfigVariants(() => makeBaseBundleConfig({ bundleType: 'addon', entrypoints: ['src/modal/integration.tsx'], diff --git a/packages/replay-canvas/rollup.bundle.config.mjs b/packages/replay-canvas/rollup.bundle.config.mjs index 557b0da35bb0..1a491abcbea4 100644 --- a/packages/replay-canvas/rollup.bundle.config.mjs +++ b/packages/replay-canvas/rollup.bundle.config.mjs @@ -1,12 +1,12 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils'; -const baseBundleConfig = makeBaseBundleConfig({ +const baseBundleOptions = { bundleType: 'addon', entrypoints: ['src/index.ts'], licenseTitle: '@sentry-internal/replay-canvas', outputFileBase: () => 'bundles/replay-canvas', -}); +}; -const builds = makeBundleConfigVariants(baseBundleConfig); +const builds = makeBundleConfigVariants(() => makeBaseBundleConfig(baseBundleOptions)); export default builds; diff --git a/packages/replay-internal/rollup.bundle.config.mjs b/packages/replay-internal/rollup.bundle.config.mjs index 9d1b9d412fed..3b5fdb974306 100644 --- a/packages/replay-internal/rollup.bundle.config.mjs +++ b/packages/replay-internal/rollup.bundle.config.mjs @@ -1,12 +1,12 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils'; -const baseBundleConfig = makeBaseBundleConfig({ +const baseBundleOptions = { bundleType: 'addon', entrypoints: ['src/index.ts'], licenseTitle: '@sentry-internal/replay', outputFileBase: () => 'bundles/replay', -}); +}; -const builds = makeBundleConfigVariants(baseBundleConfig); +const builds = makeBundleConfigVariants(() => makeBaseBundleConfig(baseBundleOptions)); export default builds; diff --git a/packages/wasm/rollup.bundle.config.mjs b/packages/wasm/rollup.bundle.config.mjs index e172565bd76f..7d9e0b3bfa8a 100644 --- a/packages/wasm/rollup.bundle.config.mjs +++ b/packages/wasm/rollup.bundle.config.mjs @@ -1,10 +1,10 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils'; -const baseBundleConfig = makeBaseBundleConfig({ +const baseBundleOptions = { bundleType: 'addon', entrypoints: ['src/index.ts'], licenseTitle: '@sentry/wasm', outputFileBase: () => 'bundles/wasm', -}); +}; -export default makeBundleConfigVariants(baseBundleConfig); +export default makeBundleConfigVariants(() => makeBaseBundleConfig(baseBundleOptions)); From c3e573af541e2bedf8ab274e545a09a32ac7e29f Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Thu, 16 Apr 2026 14:35:50 +0200 Subject: [PATCH 5/5] avoid unnecessary spread --- dev-packages/rollup-utils/bundleHelpers.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dev-packages/rollup-utils/bundleHelpers.mjs b/dev-packages/rollup-utils/bundleHelpers.mjs index 5bd9a5acbbe1..439cc5b9b401 100644 --- a/dev-packages/rollup-utils/bundleHelpers.mjs +++ b/dev-packages/rollup-utils/bundleHelpers.mjs @@ -196,8 +196,7 @@ export function makeBundleConfigVariants(getBaseConfigOrConfig, options = {}) { // default merge strategy. customMerge: key => (key === 'plugins' ? mergePlugins : undefined), }); - return { - ...merged, - }; + + return merged; }); }