From a9ab9e730ad1e38b8acc57d71c79954b01d2922a Mon Sep 17 00:00:00 2001 From: Anders Chen Date: Wed, 26 Aug 2026 19:39:54 +0000 Subject: [PATCH 1/3] fix(bundler-plugins): stabilize Rolldown debug IDs --- packages/bundler-plugins/package.json | 3 +- packages/bundler-plugins/src/rollup/index.ts | 110 ++++------- .../src/rollup/rolldown-debug-id.ts | 51 +++++ packages/bundler-plugins/src/rollup/utils.ts | 20 ++ .../src/rollup/vite-annotations.ts | 47 +++++ .../test/rollup/public-api.test.ts | 174 +++++++++++++++++- .../test/rollup/rolldown-determinism.test.ts | 118 ++++++++++++ yarn.lock | 152 ++++++++++----- 8 files changed, 549 insertions(+), 126 deletions(-) create mode 100644 packages/bundler-plugins/src/rollup/rolldown-debug-id.ts create mode 100644 packages/bundler-plugins/src/rollup/utils.ts create mode 100644 packages/bundler-plugins/src/rollup/vite-annotations.ts create mode 100644 packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts diff --git a/packages/bundler-plugins/package.json b/packages/bundler-plugins/package.json index 0c9796eefd75..9821b3b7f262 100644 --- a/packages/bundler-plugins/package.json +++ b/packages/bundler-plugins/package.json @@ -137,7 +137,8 @@ "@types/node": "^18.6.3", "@types/webpack": "npm:@types/webpack@^4", "premove": "^4.0.0", - "rolldown": "^1.0.0", + "rolldown": "1.1.2", + "rolldown-1-2": "npm:rolldown@1.2.3", "vitest": "^3.2.7", "webpack": "5.104.1" }, diff --git a/packages/bundler-plugins/src/rollup/index.ts b/packages/bundler-plugins/src/rollup/index.ts index c53ce21245bd..8ff44337bd83 100644 --- a/packages/bundler-plugins/src/rollup/index.ts +++ b/packages/bundler-plugins/src/rollup/index.ts @@ -14,89 +14,29 @@ import { replaceBooleanFlagsInCode, CodeInjection, } from '../core'; -import type { - ComponentAnnotationTransformMeta, - ComponentAnnotationTransformResult, -} from '../core/component-annotation-vite'; +import type { ComponentAnnotationTransformMeta } from '../core/component-annotation-vite'; import type { SourceMap } from 'magic-string'; import MagicString from 'magic-string'; import * as path from 'node:path'; -import { createRequire } from 'node:module'; +import { finalizeRolldownDebugIds, ROLLDOWN_DEBUG_ID_PLACEHOLDER } from './rolldown-debug-id'; +import { getRollupMajorVersion, hasExistingDebugID } from './utils'; +import { getViteParseAstAsync, type ViteAnnotationHooks } from './vite-annotations'; // The subset of Rollup's `TransformResult` that this plugin's `transform` // hook actually returns. Defined locally instead of imported from `rollup` // because `rollup` is an optional dependency. type TransformResult = { code: string; map?: SourceMap | string | { mappings: string } | null } | null | undefined; -type ViteModule = { - parseAstAsync?: (code: string, options: { lang: 'jsx' | 'tsx' }) => Promise; -}; - -type ViteParseAstAsync = NonNullable; -type ViteAnnotationHooks = { - transform( - code: string, - id: string, - meta?: ComponentAnnotationTransformMeta, - ): Promise; +type RenderChunkPluginContext = { + meta?: { + rolldownVersion?: string; + }; }; -let viteParseAstAsyncPromise: Promise | undefined; +type GenerateBundlePluginContext = RenderChunkPluginContext; const JS_MODULE_ID_FILTER = /\.[cm]?[jt]sx?(?:[?#].*)?$/; -function hasExistingDebugID(code: string): boolean { - // Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI) - const chunkStartSnippet = code.slice(0, 6000); - const chunkEndSnippet = code.slice(-500); - - if (chunkStartSnippet.includes('_sentryDebugIdIdentifier') || chunkEndSnippet.includes('//# debugId=')) { - return true; // Debug ID already present, skip injection - } - - return false; -} - -function getRollupMajorVersion(): string | undefined { - try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Rollup already transpiles this for us - const req = createRequire(import.meta.url); - const rollup = req('rollup') as { VERSION?: string }; - return rollup.VERSION?.split('.')[0]; - } catch { - // do nothing, we'll just not report a version - } - - return undefined; -} - -function getViteParseAstAsync(): Promise { - if (!viteParseAstAsyncPromise) { - viteParseAstAsyncPromise = Promise.resolve() - .then(async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Vite is an optional runtime peer for this package - const viteModule = createRequire(import.meta.url)('vite') as ViteModule; - - if (typeof viteModule.parseAstAsync !== 'function') { - return null; - } - - try { - await viteModule.parseAstAsync('const x =
;', { lang: 'tsx' }); - } catch { - return null; - } - - return viteModule.parseAstAsync; - }) - .catch(() => null); - } - - return viteParseAstAsyncPromise; -} - /** * @ignore - this is the internal plugin factory function only used for the Vite plugin! */ @@ -230,6 +170,7 @@ export function _rollupPluginInternal( } function renderChunk( + this: RenderChunkPluginContext | undefined, code: string, chunk: { fileName: string; facadeModuleId?: string | null }, _?: unknown, @@ -250,7 +191,9 @@ export function _rollupPluginInternal( const injectCode = staticInjectionCode.clone(); if (sourcemapsEnabled && !hasExistingDebugID(code)) { - const debugId = stringToUUID(code); // generate a deterministic debug ID + // Rolldown's renderChunk code contains temporary hash placeholders whose values can vary between builds. + // The fixed-width placeholder is replaced after Rolldown resolves them, without shifting source map positions. + const debugId = this?.meta?.rolldownVersion ? ROLLDOWN_DEBUG_ID_PLACEHOLDER : stringToUUID(code); injectCode.append(getDebugIdSnippet(debugId)); } @@ -280,10 +223,25 @@ export function _rollupPluginInternal( return { code: ms.toString(), - map: ms.generateMap({ file: chunk.fileName, hires: 'boundary' as unknown as undefined }), + map: ms.generateMap({ + file: chunk.fileName, + hires: 'boundary' as unknown as undefined, + }), }; } + function generateBundle( + this: GenerateBundlePluginContext | undefined, + _outputOptions: unknown, + bundle: Parameters[0], + ): void { + if (!this?.meta?.rolldownVersion) { + return; + } + + finalizeRolldownDebugIds(bundle); + } + async function writeBundle( outputOptions: { dir?: string; file?: string }, bundle: { [fileName: string]: unknown }, @@ -302,7 +260,9 @@ export function _rollupPluginInternal( '/**/*.mjs.map', '/**/*.cjs.map', ].map(q => `${q}?(\\?*)?(#*)`); // We want to allow query and hash strings at the end of files - const buildArtifacts = await globFiles(JS_AND_MAP_PATTERNS, { root: outputDir }); + const buildArtifacts = await globFiles(JS_AND_MAP_PATTERNS, { + root: outputDir, + }); await upload(buildArtifacts); } else if (outputOptions.file) { await upload([outputOptions.file]); @@ -318,6 +278,10 @@ export function _rollupPluginInternal( } const name = `sentry-${buildTool}-plugin`; + const generateBundleHook = + buildTool === 'vite' && buildToolMajorVersion === '8' + ? { order: 'post' as const, handler: generateBundle } + : generateBundle; if (shouldTransform) { const transformHook = @@ -333,6 +297,7 @@ export function _rollupPluginInternal( buildStart, transform: transformHook, renderChunk, + generateBundle: generateBundleHook, writeBundle, }; } @@ -341,6 +306,7 @@ export function _rollupPluginInternal( name, buildStart, renderChunk, + generateBundle: generateBundleHook, writeBundle, }; } diff --git a/packages/bundler-plugins/src/rollup/rolldown-debug-id.ts b/packages/bundler-plugins/src/rollup/rolldown-debug-id.ts new file mode 100644 index 000000000000..26122f1aae09 --- /dev/null +++ b/packages/bundler-plugins/src/rollup/rolldown-debug-id.ts @@ -0,0 +1,51 @@ +import { stringToUUID } from '../core'; + +export const ROLLDOWN_DEBUG_ID_PLACEHOLDER = 'SENTRY_DEBUG_ID_PLACEHOLDER_00000000'; + +type GeneratedBundle = Record< + string, + { + type?: string; + fileName?: string; + code?: string; + } +>; + +const SENTRY_DEBUG_ID_IDENTIFIER = '_sentryDebugIdIdentifier'; +const SENTRY_DEBUG_ID_IDENTIFIER_PREFIX = 'sentry-dbid-'; + +function replaceAt(code: string, start: number, search: string, replacement: string): string { + return `${code.slice(0, start)}${replacement}${code.slice(start + search.length)}`; +} + +export function finalizeRolldownDebugIds(bundle: GeneratedBundle): void { + for (const [fileName, chunk] of Object.entries(bundle)) { + if (chunk.type !== 'chunk' || !chunk.code) { + continue; + } + + const identifier = `${SENTRY_DEBUG_ID_IDENTIFIER_PREFIX}${ROLLDOWN_DEBUG_ID_PLACEHOLDER}`; + const identifierPropertyStart = chunk.code.indexOf(SENTRY_DEBUG_ID_IDENTIFIER); + const identifierStart = chunk.code.indexOf(identifier, identifierPropertyStart + SENTRY_DEBUG_ID_IDENTIFIER.length); + if (identifierStart === -1) { + continue; + } + + const identifierPlaceholderStart = identifierStart + SENTRY_DEBUG_ID_IDENTIFIER_PREFIX.length; + const debugIdsPlaceholderStart = chunk.code.lastIndexOf(ROLLDOWN_DEBUG_ID_PLACEHOLDER, identifierStart - 1); + if (debugIdsPlaceholderStart === -1) { + throw new Error(`Failed to locate the Sentry debug ID placeholder for chunk \`${fileName}\`.`); + } + + // Including the final filename disambiguates otherwise identical chunks. The fixed-width replacement deliberately + // happens after Rolldown computes [hash], so the emitted filename represents the placeholder-bearing chunk. + const debugId = stringToUUID(JSON.stringify([chunk.fileName ?? fileName, chunk.code])); + const codeWithIdentifier = replaceAt( + chunk.code, + identifierPlaceholderStart, + ROLLDOWN_DEBUG_ID_PLACEHOLDER, + debugId, + ); + chunk.code = replaceAt(codeWithIdentifier, debugIdsPlaceholderStart, ROLLDOWN_DEBUG_ID_PLACEHOLDER, debugId); + } +} diff --git a/packages/bundler-plugins/src/rollup/utils.ts b/packages/bundler-plugins/src/rollup/utils.ts new file mode 100644 index 000000000000..e2c93ef1855f --- /dev/null +++ b/packages/bundler-plugins/src/rollup/utils.ts @@ -0,0 +1,20 @@ +import { createRequire } from 'node:module'; + +export function hasExistingDebugID(code: string): boolean { + const chunkStartSnippet = code.slice(0, 6000); + const chunkEndSnippet = code.slice(-500); + + return chunkStartSnippet.includes('_sentryDebugIdIdentifier') || chunkEndSnippet.includes('//# debugId='); +} + +export function getRollupMajorVersion(): string | undefined { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Rollup already transpiles this for us + const req = createRequire(import.meta.url); + const rollup = req('rollup') as { VERSION?: string }; + return rollup.VERSION?.split('.')[0]; + } catch { + return undefined; + } +} diff --git a/packages/bundler-plugins/src/rollup/vite-annotations.ts b/packages/bundler-plugins/src/rollup/vite-annotations.ts new file mode 100644 index 000000000000..ca97bed9cfba --- /dev/null +++ b/packages/bundler-plugins/src/rollup/vite-annotations.ts @@ -0,0 +1,47 @@ +import { createRequire } from 'node:module'; +import type { + ComponentAnnotationTransformMeta, + ComponentAnnotationTransformResult, +} from '../core/component-annotation-vite'; + +type ViteModule = { + parseAstAsync?: (code: string, options: { lang: 'jsx' | 'tsx' }) => Promise; +}; + +type ViteParseAstAsync = NonNullable; + +export type ViteAnnotationHooks = { + transform( + code: string, + id: string, + meta?: ComponentAnnotationTransformMeta, + ): Promise; +}; + +let viteParseAstAsyncPromise: Promise | undefined; + +export function getViteParseAstAsync(): Promise { + if (!viteParseAstAsyncPromise) { + viteParseAstAsyncPromise = Promise.resolve() + .then(async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Vite is an optional runtime peer for this package + const viteModule = createRequire(import.meta.url)('vite') as ViteModule; + + if (typeof viteModule.parseAstAsync !== 'function') { + return null; + } + + try { + await viteModule.parseAstAsync('const x =
;', { lang: 'tsx' }); + } catch { + return null; + } + + return viteModule.parseAstAsync; + }) + .catch(() => null); + } + + return viteParseAstAsyncPromise; +} diff --git a/packages/bundler-plugins/test/rollup/public-api.test.ts b/packages/bundler-plugins/test/rollup/public-api.test.ts index b54077fce2bc..24060d966bea 100644 --- a/packages/bundler-plugins/test/rollup/public-api.test.ts +++ b/packages/bundler-plugins/test/rollup/public-api.test.ts @@ -9,7 +9,10 @@ const { babelCoreImportMock, transformAsyncMock, viteAnnotationModuleImportMock, babelCoreImportMock: vi.fn(), transformAsyncMock: vi.fn(async (code: string) => ({ code, map: null })), viteAnnotationModuleImportMock: vi.fn(), - viteAnnotationTransformMock: vi.fn(async () => ({ code: 'fast-path', map: null })), + viteAnnotationTransformMock: vi.fn(async () => ({ + code: 'fast-path', + map: null, + })), }; }); @@ -101,6 +104,12 @@ test('uses a Rollup 3-compatible function transform hook for Rollup builds', () expect(typeof vitePlugin.transform).toBe('object'); }); +test('runs Vite 8 debug ID finalization after other generateBundle hooks', () => { + const vitePlugin = _rollupPluginInternal({ release: { inject: false } }, 'vite', '8') as Plugin; + + expect(vitePlugin.generateBundle).toMatchObject({ order: 'post' }); +}); + describe('sentryRollupPlugin', () => { beforeEach(() => { vi.clearAllMocks(); @@ -123,7 +132,7 @@ describe('sentryRollupPlugin', () => { describe('Hooks', () => { const [plugin] = sentryRollupPlugin({ release: { inject: false } }) as [Plugin]; - const renderChunk = plugin.renderChunk as ( + const renderChunk = plugin.renderChunk as unknown as ( code: string, chunkInfo: { fileName: string; facadeModuleId?: string }, ) => { @@ -294,7 +303,9 @@ bootstrap();`; }); it('should inject into regular JS chunks (no HTML facade)', () => { - const result = renderChunk(`console.log("Hello");`, { fileName: 'bundle.js' }); + const result = renderChunk(`console.log("Hello");`, { + fileName: 'bundle.js', + }); expect(result).not.toBeNull(); expect(result?.code).toMatchInlineSnapshot( `"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="79f18a7f-ca16-4168-9797-906c82058367",e._sentryDebugIdIdentifier="sentry-dbid-79f18a7f-ca16-4168-9797-906c82058367");}catch(e){}}();console.log("Hello");"`, @@ -303,3 +314,160 @@ bootstrap();`; }); }); }); + +describe('Rolldown debug ID finalization', () => { + const [plugin] = sentryRollupPlugin({ + release: { inject: false }, + sourcemaps: { disable: 'disable-upload' }, + }) as [Plugin]; + const rolldownContext = { meta: { rolldownVersion: '1.2.3' } }; + const rollupContext: { meta: { rolldownVersion?: string } } = { meta: {} }; + const renderChunk = plugin.renderChunk as unknown as ( + this: typeof rolldownContext, + code: string, + chunkInfo: { fileName: string }, + ) => { code: string } | null; + const generateBundle = plugin.generateBundle as ( + this: typeof rollupContext, + outputOptions: unknown, + bundle: Record, + ) => void; + + function extractDebugId(code: string): string { + const debugIds = code.match(/[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/g); + expect(debugIds).toHaveLength(2); + expect(new Set(debugIds)).toHaveLength(1); + + return debugIds?.[0] ?? ''; + } + + function renderAndFinalize(code: string, fileName: string): string { + const result = renderChunk.call(rolldownContext, code, { fileName }); + expect(result).not.toBeNull(); + + const bundle = { + [fileName]: { + type: 'chunk' as const, + fileName, + code: result?.code || '', + }, + }; + generateBundle.call(rolldownContext, {}, bundle); + + return bundle[fileName]?.code ?? ''; + } + + it('defers the debug ID until Rolldown has finalized the chunk', () => { + const result = renderChunk.call(rolldownContext, 'console.log("test");', { + fileName: 'bundle.js', + }); + + expect(result?.code).toContain('SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); + + const finalizedCode = renderAndFinalize('console.log("test");', 'bundle.js'); + expect(finalizedCode).not.toContain('SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); + expect(extractDebugId(finalizedCode)).not.toBe(''); + }); + + it('generates stable IDs from finalized code and filenames', () => { + const firstBuild = renderAndFinalize('console.log("test");', 'bundle.js'); + const secondBuild = renderAndFinalize('console.log("test");', 'bundle.js'); + + expect(extractDebugId(firstBuild)).toBe(extractDebugId(secondBuild)); + }); + + it('changes the ID when finalized code changes', () => { + const firstBuild = renderAndFinalize('console.log("first");', 'bundle.js'); + const secondBuild = renderAndFinalize('console.log("second");', 'bundle.js'); + + expect(extractDebugId(firstBuild)).not.toBe(extractDebugId(secondBuild)); + }); + + it('assigns different IDs to identical chunks with different filenames', () => { + const firstChunk = renderAndFinalize('console.log("test");', 'first.js'); + const secondChunk = renderAndFinalize('console.log("test");', 'second.js'); + + expect(extractDebugId(firstChunk)).not.toBe(extractDebugId(secondChunk)); + }); + + it('does not replace placeholder-shaped strings in user code', () => { + const userCode = 'console.log("SENTRY_DEBUG_ID_PLACEHOLDER_00000000");'; + const finalizedCode = renderAndFinalize(userCode, 'bundle.js'); + + expect(finalizedCode).toContain(userCode); + }); + + it('does not replace placeholder-shaped strings before the injected snippet', () => { + const userCode = '// SENTRY_DEBUG_ID_PLACEHOLDER_00000000\nconsole.log("test");'; + const finalizedCode = renderAndFinalize(userCode, 'bundle.js'); + + expect(finalizedCode).toContain('// SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); + expect(finalizedCode).toContain('console.log("test");'); + expect(extractDebugId(finalizedCode)).not.toBe(''); + }); + + it('does not replace a marker-shaped string before the injected identifier', () => { + const userCode = '// sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000\nconsole.log("test");'; + const finalizedCode = renderAndFinalize(userCode, 'bundle.js'); + + expect(finalizedCode).toContain('// sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); + expect(finalizedCode).toContain('console.log("test");'); + expect(extractDebugId(finalizedCode)).not.toBe(''); + }); + + it('does not finalize marker-shaped Rollup user code', () => { + const code = 'console.log("sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000");'; + const bundle = { + 'bundle.js': { type: 'chunk' as const, fileName: 'bundle.js', code }, + }; + + generateBundle.call(rollupContext, {}, bundle); + + expect(bundle['bundle.js'].code).toBe(code); + }); + + it('fails when the injected debug ID placeholder is incomplete', () => { + const bundle = { + 'bundle.js': { + type: 'chunk' as const, + fileName: 'bundle.js', + code: 'globalThis._sentryDebugIdIdentifier="sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000";', + }, + }; + + expect(() => generateBundle.call(rolldownContext, {}, bundle)).toThrow( + 'Failed to locate the Sentry debug ID placeholder for chunk `bundle.js`.', + ); + }); + + it('ignores non-chunk assets', () => { + const code = 'sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000'; + const bundle = { + 'asset.js': { type: 'asset' as const, fileName: 'asset.js', code }, + }; + + generateBundle.call(rolldownContext, {}, bundle as never); + + expect(bundle['asset.js'].code).toBe(code); + }); + + it('leaves existing debug IDs untouched in Rolldown', () => { + const code = 'globalThis._sentryDebugIdIdentifier="sentry-dbid-f6ccd6f4-7ea0-4854-8384-1c9f8340af81";'; + + expect(renderChunk.call(rolldownContext, code, { fileName: 'bundle.js' })).toBeNull(); + }); + + it('does not inject a placeholder when sourcemaps are disabled', () => { + const [disabledPlugin] = sentryRollupPlugin({ + release: { inject: false }, + sourcemaps: { disable: true }, + }) as [Plugin]; + const disabledRenderChunk = disabledPlugin.renderChunk as unknown as typeof renderChunk; + + expect( + disabledRenderChunk.call(rolldownContext, 'console.log("test");', { + fileName: 'bundle.js', + }), + ).toBeNull(); + }); +}); diff --git a/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts b/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts new file mode 100644 index 000000000000..61a0da0e90a5 --- /dev/null +++ b/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts @@ -0,0 +1,118 @@ +import { rolldown as rolldown112 } from 'rolldown'; +import { rolldown as rolldown123 } from 'rolldown-1-2'; +import { describe, expect, it } from 'vitest'; +import { sentryRollupPlugin } from '../../src/rollup'; + +const virtualModules: Record = { + 'virtual:app': `export async function loadShared() { return import('virtual:shared'); }`, + 'virtual:shared': `export const shared = 'shared';`, + 'virtual:unrelated': `console.log('unrelated');`, +}; + +type RolldownVersion = '1.1.2' | '1.2.3'; +type SourceMapMode = boolean | 'inline' | 'hidden'; +type OutputFormat = 'esm' | 'cjs'; + +const outputCases: [OutputFormat, SourceMapMode][] = [ + ['esm', true], + ['esm', false], + ['esm', 'inline'], + ['esm', 'hidden'], + ['cjs', true], + ['cjs', false], + ['cjs', 'inline'], + ['cjs', 'hidden'], +]; + +async function createBuild(version: RolldownVersion, includeUnrelatedEntry: boolean) { + const input: Record = includeUnrelatedEntry + ? { unrelated: 'virtual:unrelated', app: 'virtual:app' } + : { app: 'virtual:app' }; + const options = { + input, + plugins: [ + { + name: 'virtual-modules', + resolveId(id: string) { + return id in virtualModules ? id : null; + }, + load(id: string) { + return virtualModules[id] ?? null; + }, + }, + ...sentryRollupPlugin({ + release: { inject: false }, + sourcemaps: { disable: 'disable-upload' }, + telemetry: false, + }), + ], + }; + + return version === '1.1.2' ? rolldown112(options) : rolldown123(options); +} + +async function build( + version: RolldownVersion, + includeUnrelatedEntry: boolean, + sourcemap: SourceMapMode = true, + format: OutputFormat = 'esm', +) { + const bundle = await createBuild(version, includeUnrelatedEntry); + + try { + const { output } = await bundle.generate({ + format, + sourcemap, + entryFileNames: '[name]-[hash].js', + chunkFileNames: '[name]-[hash].js', + }); + + return output.filter(outputFile => outputFile.type === 'chunk'); + } finally { + await bundle.close(); + } +} + +function expectFinalizedDebugId(code: string): void { + expect(code).not.toContain('SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); + const debugIds = code.match(/[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/g); + expect(debugIds).toHaveLength(2); + expect(new Set(debugIds)).toHaveLength(1); +} + +describe.each(['1.1.2', '1.2.3'] satisfies RolldownVersion[])('Rolldown %s debug ID determinism', version => { + it.each(outputCases)( + 'produces identical %s chunks in repeated builds with sourcemap=%s', + async (format, sourcemap) => { + const firstBuild = await build(version, false, sourcemap, format); + const secondBuild = await build(version, false, sourcemap, format); + + const comparableOutput = (chunks: typeof firstBuild) => + chunks.map(chunk => ({ + fileName: chunk.fileName, + code: chunk.code, + map: chunk.map?.toString(), + })); + expect(comparableOutput(secondBuild)).toEqual(comparableOutput(firstBuild)); + for (const chunk of firstBuild) { + expectFinalizedDebugId(chunk.code); + } + }, + ); + + it('keeps existing chunks stable when an unrelated entry changes placeholder allocation', async () => { + const firstBuild = await build(version, false); + const secondBuild = await build(version, true); + + for (const facadeModuleId of ['virtual:app', 'virtual:shared']) { + const firstChunk = firstBuild.find(chunk => chunk.facadeModuleId === facadeModuleId); + const secondChunk = secondBuild.find(chunk => chunk.facadeModuleId === facadeModuleId); + + expect(firstChunk).toBeDefined(); + expect(secondChunk).toBeDefined(); + expect(secondChunk?.fileName).toBe(firstChunk?.fileName); + expect(secondChunk?.code).toBe(firstChunk?.code); + expectFinalizedDebugId(firstChunk?.code ?? ''); + } + }); +}); diff --git a/yarn.lock b/yarn.lock index 0fb22843e4ea..b3f415d33fa9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3103,7 +3103,7 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260426.1.tgz#b21d2a24afe0b274982d7ccbe7163a5689da1507" integrity sha512-d3Xj/IjINRgNVwH+eKhpUn4xkkcEewbWXbOvBlapiirKWh5zl9m0Epi3qOqmjyRYK6MICqIGXg4qZBEt0lxudw== -"@cloudflare/workers-types-v5@npm:@cloudflare/workers-types@5.20260710.1": +"@cloudflare/workers-types-v5@npm:@cloudflare/workers-types@5.20260710.1", "@cloudflare/workers-types@5.20260710.1": version "5.20260710.1" resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-5.20260710.1.tgz#215c0cf84c3917552b53a1f5129150abf0b6009f" integrity sha512-4ooaY2Pb5XGwDn8Fzm6jnTAJkIX0R5LBvL9euQpp2T58sQItlAQd9yivAlkwGhpY5cM1u81/9HaXwKAjXwtyzA== @@ -3118,11 +3118,6 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-4.20250922.0.tgz#a159fbf3bb785fa85b473ecfaa8c501525827885" integrity sha512-BaqlKnVc0Xzqm9xt3TC4v0yB9EHy5vVqpiWz+DAsbEmdcpUbqdBschvI9502p6FgFbZElD7XcxTEeViXLsoO0A== -"@cloudflare/workers-types@5.20260710.1": - version "5.20260710.1" - resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-5.20260710.1.tgz#215c0cf84c3917552b53a1f5129150abf0b6009f" - integrity sha512-4ooaY2Pb5XGwDn8Fzm6jnTAJkIX0R5LBvL9euQpp2T58sQItlAQd9yivAlkwGhpY5cM1u81/9HaXwKAjXwtyzA== - "@cloudflare/workers-types@^4.20260426.0": version "4.20260519.1" resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-4.20260519.1.tgz#061b4594e874a0e506ddc6599221939e6718d2a7" @@ -6635,7 +6630,7 @@ resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.137.0.tgz#56e77f8bb221fa05f18b1cd34d73f94f0954a773" integrity sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA== -"@oxc-project/types@^0.143.0": +"@oxc-project/types@=0.143.0", "@oxc-project/types@^0.143.0": version "0.143.0" resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.143.0.tgz#c3e4f3178b7b54e4dd194eac6d45258a60f0092b" integrity sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA== @@ -7522,61 +7517,121 @@ resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.2.tgz#88fd6b295a411e62b7926433a45eb5e17e68bba4" integrity sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA== +"@rolldown/binding-android-arm64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz#001b8b0b01844701efda1bb6bed84b681c4a488b" + integrity sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw== + "@rolldown/binding-darwin-arm64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.2.tgz#2f840f7e6501cb52370411c2fb008119f1fbf400" integrity sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw== +"@rolldown/binding-darwin-arm64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz#5e87c602ed634a6fef092e2162e24fbfb881c4ec" + integrity sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA== + "@rolldown/binding-darwin-x64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.2.tgz#2ed3b66dded5140d22ca2ac58d4e1c1e3143f490" integrity sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw== +"@rolldown/binding-darwin-x64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz#f32e0b286714bd03a421d693415d05d97d265b77" + integrity sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA== + "@rolldown/binding-freebsd-x64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.2.tgz#d3d8603ae480a505eb8c12643c901b2a3771b875" integrity sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA== +"@rolldown/binding-freebsd-x64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz#5f38ad5761b6b7b21b57a99566bb52634c60ab19" + integrity sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg== + "@rolldown/binding-linux-arm-gnueabihf@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.2.tgz#bc36e0e33566bc80877fe4bca56fd32b241dbacb" integrity sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw== +"@rolldown/binding-linux-arm-gnueabihf@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz#ab4dcd07f1bd88e8d659ae0c3bb9d2f290adb897" + integrity sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw== + "@rolldown/binding-linux-arm64-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.2.tgz#6267a447e6bfc5a99eb030a6a99194ecc917a652" integrity sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw== +"@rolldown/binding-linux-arm64-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz#d279b7016039a725fb66d82784b9841f42df83da" + integrity sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw== + "@rolldown/binding-linux-arm64-musl@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.2.tgz#68fc068f5ebc1d137eecdb651d90830f330aad48" integrity sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ== +"@rolldown/binding-linux-arm64-musl@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz#d08bbc93d2742214548c5adf7df7788944e5a89a" + integrity sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q== + "@rolldown/binding-linux-ppc64-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.2.tgz#fe072f0bc3b713ae25b357651ced38d39e3d81e0" integrity sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ== +"@rolldown/binding-linux-ppc64-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz#6418e63745b3193f26ab3bb88744b3a4a1356d7c" + integrity sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w== + "@rolldown/binding-linux-s390x-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.2.tgz#9492609384775c6edec9bfbe5b1cbba9660dddcc" integrity sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg== +"@rolldown/binding-linux-s390x-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz#77ec30d0704cf4eb1cb4a63f501c9852c6728cf4" + integrity sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg== + "@rolldown/binding-linux-x64-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.2.tgz#74029d9f86d60fa9bcf2670b1480e22438203c98" integrity sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw== +"@rolldown/binding-linux-x64-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz#3b9b6e0dd3e86c597f42858748ca25f1dfd58ed8" + integrity sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w== + "@rolldown/binding-linux-x64-musl@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.2.tgz#eb3004015027a7af12f9b0ac85ffa1634015c873" integrity sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw== +"@rolldown/binding-linux-x64-musl@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz#f78033c592c8bd2af48284a45f8e4baaa0befbf5" + integrity sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A== + "@rolldown/binding-openharmony-arm64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.2.tgz#6a02c49dc0f698614f97c68c6f7c21aadc7600b1" integrity sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw== +"@rolldown/binding-openharmony-arm64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz#36e951f5a6fca922a5205e283d0a82b9f98199ca" + integrity sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug== + "@rolldown/binding-wasm32-wasi@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.2.tgz#3f67c083e0762b8cd6c95e8edfe1a743d7ffdf78" @@ -7591,11 +7646,21 @@ resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.2.tgz#a69c5a03b3ba36cb0b1154709293e0cefc9dd69c" integrity sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg== +"@rolldown/binding-win32-arm64-msvc@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz#c1e494ac47e13bd857fca0b3ad59c33580241f7e" + integrity sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw== + "@rolldown/binding-win32-x64-msvc@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.2.tgz#e1d9a6ccd29de00378f8dd6e275adbde5731d30a" integrity sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA== +"@rolldown/binding-win32-x64-msvc@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz#b0effffcd6872f8a021373eb437916b1b52283a4" + integrity sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg== + "@rolldown/pluginutils@^1.0.0", "@rolldown/pluginutils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz#e3fcee093fbb5ce765e1ad088ff4de2889f6f9be" @@ -9263,12 +9328,7 @@ dependencies: "@types/unist" "*" -"@types/history-4@npm:@types/history@4.7.8": - version "4.7.8" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" - integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== - -"@types/history-5@npm:@types/history@4.7.8": +"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -23497,7 +23557,8 @@ react-refresh@^0.14.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -"react-router-6@npm:react-router@6.30.4": +"react-router-6@npm:react-router@6.30.4", react-router@6.30.4: + name react-router-6 version "6.30.4" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.30.4.tgz#638f35176527bd243d96d81d35d33b757bad46c2" integrity sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA== @@ -23512,13 +23573,6 @@ react-router-dom@6.30.4: "@remix-run/router" "1.23.3" react-router "6.30.4" -react-router@6.30.4: - version "6.30.4" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.30.4.tgz#638f35176527bd243d96d81d35d33b757bad46c2" - integrity sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA== - dependencies: - "@remix-run/router" "1.23.3" - react-router@^7.18.0: version "7.18.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.18.0.tgz#e7d94b54745277aabe3cf93fac938cbebc9c1c5e" @@ -24288,7 +24342,30 @@ roarr@^7.0.4: safe-stable-stringify "^2.4.1" semver-compare "^1.0.0" -rolldown@^1.0.0, rolldown@^1.0.0-rc.15, rolldown@^1.0.0-rc.8: +"rolldown-1-2@npm:rolldown@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.3.tgz#103bdcbbd575d51265277b8b510f080827b6eb6f" + integrity sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A== + dependencies: + "@oxc-project/types" "=0.143.0" + "@rolldown/pluginutils" "^1.0.0" + optionalDependencies: + "@rolldown/binding-android-arm64" "1.2.3" + "@rolldown/binding-darwin-arm64" "1.2.3" + "@rolldown/binding-darwin-x64" "1.2.3" + "@rolldown/binding-freebsd-x64" "1.2.3" + "@rolldown/binding-linux-arm-gnueabihf" "1.2.3" + "@rolldown/binding-linux-arm64-gnu" "1.2.3" + "@rolldown/binding-linux-arm64-musl" "1.2.3" + "@rolldown/binding-linux-ppc64-gnu" "1.2.3" + "@rolldown/binding-linux-s390x-gnu" "1.2.3" + "@rolldown/binding-linux-x64-gnu" "1.2.3" + "@rolldown/binding-linux-x64-musl" "1.2.3" + "@rolldown/binding-openharmony-arm64" "1.2.3" + "@rolldown/binding-win32-arm64-msvc" "1.2.3" + "@rolldown/binding-win32-x64-msvc" "1.2.3" + +rolldown@1.1.2, rolldown@^1.0.0-rc.15, rolldown@^1.0.0-rc.8: version "1.1.2" resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.1.2.tgz#accb41e26c872ad2c5198a39c1281c7b6b844097" integrity sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ== @@ -25569,16 +25646,7 @@ string-similarity@^4.0.1: resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -25681,14 +25749,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28288,16 +28349,7 @@ wrangler@4.86.0: optionalDependencies: fsevents "~2.3.2" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@7.0.0, wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 5de7b04eeee37a7c0492af81dd4021ffbfc3fc2c Mon Sep 17 00:00:00 2001 From: Anders Chen Date: Wed, 26 Aug 2026 19:50:16 +0000 Subject: [PATCH 2/3] test(bundler-plugins): cover Rolldown 1.2.5 --- packages/bundler-plugins/package.json | 2 +- .../test/rollup/public-api.test.ts | 2 +- .../test/rollup/rolldown-determinism.test.ts | 4 +- yarn.lock | 163 ++++++++++-------- 4 files changed, 91 insertions(+), 80 deletions(-) diff --git a/packages/bundler-plugins/package.json b/packages/bundler-plugins/package.json index 9821b3b7f262..014d09854f1c 100644 --- a/packages/bundler-plugins/package.json +++ b/packages/bundler-plugins/package.json @@ -138,7 +138,7 @@ "@types/webpack": "npm:@types/webpack@^4", "premove": "^4.0.0", "rolldown": "1.1.2", - "rolldown-1-2": "npm:rolldown@1.2.3", + "rolldown-1-2": "npm:rolldown@1.2.5", "vitest": "^3.2.7", "webpack": "5.104.1" }, diff --git a/packages/bundler-plugins/test/rollup/public-api.test.ts b/packages/bundler-plugins/test/rollup/public-api.test.ts index 24060d966bea..630bbd2b15c6 100644 --- a/packages/bundler-plugins/test/rollup/public-api.test.ts +++ b/packages/bundler-plugins/test/rollup/public-api.test.ts @@ -320,7 +320,7 @@ describe('Rolldown debug ID finalization', () => { release: { inject: false }, sourcemaps: { disable: 'disable-upload' }, }) as [Plugin]; - const rolldownContext = { meta: { rolldownVersion: '1.2.3' } }; + const rolldownContext = { meta: { rolldownVersion: '1.2.5' } }; const rollupContext: { meta: { rolldownVersion?: string } } = { meta: {} }; const renderChunk = plugin.renderChunk as unknown as ( this: typeof rolldownContext, diff --git a/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts b/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts index 61a0da0e90a5..9dc8ef4b3f2b 100644 --- a/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts +++ b/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts @@ -9,7 +9,7 @@ const virtualModules: Record = { 'virtual:unrelated': `console.log('unrelated');`, }; -type RolldownVersion = '1.1.2' | '1.2.3'; +type RolldownVersion = '1.1.2' | '1.2.5'; type SourceMapMode = boolean | 'inline' | 'hidden'; type OutputFormat = 'esm' | 'cjs'; @@ -80,7 +80,7 @@ function expectFinalizedDebugId(code: string): void { expect(new Set(debugIds)).toHaveLength(1); } -describe.each(['1.1.2', '1.2.3'] satisfies RolldownVersion[])('Rolldown %s debug ID determinism', version => { +describe.each(['1.1.2', '1.2.5'] satisfies RolldownVersion[])('Rolldown %s debug ID determinism', version => { it.each(outputCases)( 'produces identical %s chunks in repeated builds with sourcemap=%s', async (format, sourcemap) => { diff --git a/yarn.lock b/yarn.lock index b3f415d33fa9..2dfabe9a5d81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6630,7 +6630,12 @@ resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.137.0.tgz#56e77f8bb221fa05f18b1cd34d73f94f0954a773" integrity sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA== -"@oxc-project/types@=0.143.0", "@oxc-project/types@^0.143.0": +"@oxc-project/types@=0.146.0": + version "0.146.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.146.0.tgz#d57a2591abbf1f6e50981b07ee24ab269530d87a" + integrity sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA== + +"@oxc-project/types@^0.143.0": version "0.143.0" resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.143.0.tgz#c3e4f3178b7b54e4dd194eac6d45258a60f0092b" integrity sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA== @@ -7512,125 +7517,130 @@ dependencies: web-streams-polyfill "^3.1.1" +"@rolldown/binding-android-arm-eabi@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz#165b80910de7cd33f772d5b7b045b259acb7420c" + integrity sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA== + "@rolldown/binding-android-arm64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.2.tgz#88fd6b295a411e62b7926433a45eb5e17e68bba4" integrity sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA== -"@rolldown/binding-android-arm64@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz#001b8b0b01844701efda1bb6bed84b681c4a488b" - integrity sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw== +"@rolldown/binding-android-arm64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz#5ed74d4b8fa56c68eb1aeb81d0d207a85b6de05c" + integrity sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig== "@rolldown/binding-darwin-arm64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.2.tgz#2f840f7e6501cb52370411c2fb008119f1fbf400" integrity sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw== -"@rolldown/binding-darwin-arm64@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz#5e87c602ed634a6fef092e2162e24fbfb881c4ec" - integrity sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA== +"@rolldown/binding-darwin-arm64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz#6f27c7060e58ca03061fa7d50f9dc409bc377fe1" + integrity sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww== "@rolldown/binding-darwin-x64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.2.tgz#2ed3b66dded5140d22ca2ac58d4e1c1e3143f490" integrity sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw== -"@rolldown/binding-darwin-x64@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz#f32e0b286714bd03a421d693415d05d97d265b77" - integrity sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA== +"@rolldown/binding-darwin-x64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz#74ab897d134ede4072fdc6108f00193e6167ee28" + integrity sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A== "@rolldown/binding-freebsd-x64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.2.tgz#d3d8603ae480a505eb8c12643c901b2a3771b875" integrity sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA== -"@rolldown/binding-freebsd-x64@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz#5f38ad5761b6b7b21b57a99566bb52634c60ab19" - integrity sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg== +"@rolldown/binding-freebsd-x64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz#7d337f9ae4b739674e1938747118ab0676c8ef8a" + integrity sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw== "@rolldown/binding-linux-arm-gnueabihf@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.2.tgz#bc36e0e33566bc80877fe4bca56fd32b241dbacb" integrity sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw== -"@rolldown/binding-linux-arm-gnueabihf@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz#ab4dcd07f1bd88e8d659ae0c3bb9d2f290adb897" - integrity sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw== +"@rolldown/binding-linux-arm-gnueabihf@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz#a8195dc1091ade0f912b0613ef6500941e5615f4" + integrity sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg== "@rolldown/binding-linux-arm64-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.2.tgz#6267a447e6bfc5a99eb030a6a99194ecc917a652" integrity sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw== -"@rolldown/binding-linux-arm64-gnu@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz#d279b7016039a725fb66d82784b9841f42df83da" - integrity sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw== +"@rolldown/binding-linux-arm64-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz#40fbbb97072b1acaa3e0e8fe9774fe524e860bba" + integrity sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA== "@rolldown/binding-linux-arm64-musl@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.2.tgz#68fc068f5ebc1d137eecdb651d90830f330aad48" integrity sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ== -"@rolldown/binding-linux-arm64-musl@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz#d08bbc93d2742214548c5adf7df7788944e5a89a" - integrity sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q== +"@rolldown/binding-linux-arm64-musl@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz#3cfe8b0f7c13de29dace9a9fc6c03081164176ab" + integrity sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA== "@rolldown/binding-linux-ppc64-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.2.tgz#fe072f0bc3b713ae25b357651ced38d39e3d81e0" integrity sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ== -"@rolldown/binding-linux-ppc64-gnu@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz#6418e63745b3193f26ab3bb88744b3a4a1356d7c" - integrity sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w== +"@rolldown/binding-linux-ppc64-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz#3bd890d96ae29f93718aa142ed0982f2ee2978ad" + integrity sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA== "@rolldown/binding-linux-s390x-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.2.tgz#9492609384775c6edec9bfbe5b1cbba9660dddcc" integrity sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg== -"@rolldown/binding-linux-s390x-gnu@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz#77ec30d0704cf4eb1cb4a63f501c9852c6728cf4" - integrity sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg== +"@rolldown/binding-linux-s390x-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz#0959a0d5af22741d5787918e27aef70dc8602804" + integrity sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ== "@rolldown/binding-linux-x64-gnu@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.2.tgz#74029d9f86d60fa9bcf2670b1480e22438203c98" integrity sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw== -"@rolldown/binding-linux-x64-gnu@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz#3b9b6e0dd3e86c597f42858748ca25f1dfd58ed8" - integrity sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w== +"@rolldown/binding-linux-x64-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz#7baa94e826328ac6f457d9e1abacffa0353e8d22" + integrity sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ== "@rolldown/binding-linux-x64-musl@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.2.tgz#eb3004015027a7af12f9b0ac85ffa1634015c873" integrity sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw== -"@rolldown/binding-linux-x64-musl@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz#f78033c592c8bd2af48284a45f8e4baaa0befbf5" - integrity sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A== +"@rolldown/binding-linux-x64-musl@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz#5f37597eaf0e22313d1e3d4be7d1be1fd332904e" + integrity sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA== "@rolldown/binding-openharmony-arm64@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.2.tgz#6a02c49dc0f698614f97c68c6f7c21aadc7600b1" integrity sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw== -"@rolldown/binding-openharmony-arm64@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz#36e951f5a6fca922a5205e283d0a82b9f98199ca" - integrity sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug== +"@rolldown/binding-openharmony-arm64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz#d096567af3f738cbe6aa858ab0a01aae9f357ef4" + integrity sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw== "@rolldown/binding-wasm32-wasi@1.1.2": version "1.1.2" @@ -7646,20 +7656,20 @@ resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.2.tgz#a69c5a03b3ba36cb0b1154709293e0cefc9dd69c" integrity sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg== -"@rolldown/binding-win32-arm64-msvc@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz#c1e494ac47e13bd857fca0b3ad59c33580241f7e" - integrity sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw== +"@rolldown/binding-win32-arm64-msvc@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz#d53b911aa4e6b547b789c07747fabab2ac8c237d" + integrity sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw== "@rolldown/binding-win32-x64-msvc@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.2.tgz#e1d9a6ccd29de00378f8dd6e275adbde5731d30a" integrity sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA== -"@rolldown/binding-win32-x64-msvc@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz#b0effffcd6872f8a021373eb437916b1b52283a4" - integrity sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg== +"@rolldown/binding-win32-x64-msvc@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz#7bbd08cfda6a98de9b756472c772a36ebb7229bc" + integrity sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw== "@rolldown/pluginutils@^1.0.0", "@rolldown/pluginutils@^1.0.1": version "1.0.1" @@ -24342,28 +24352,29 @@ roarr@^7.0.4: safe-stable-stringify "^2.4.1" semver-compare "^1.0.0" -"rolldown-1-2@npm:rolldown@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.3.tgz#103bdcbbd575d51265277b8b510f080827b6eb6f" - integrity sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A== +"rolldown-1-2@npm:rolldown@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.5.tgz#1f504a7d05260a769e617d950410bbb051498c50" + integrity sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA== dependencies: - "@oxc-project/types" "=0.143.0" + "@oxc-project/types" "=0.146.0" "@rolldown/pluginutils" "^1.0.0" optionalDependencies: - "@rolldown/binding-android-arm64" "1.2.3" - "@rolldown/binding-darwin-arm64" "1.2.3" - "@rolldown/binding-darwin-x64" "1.2.3" - "@rolldown/binding-freebsd-x64" "1.2.3" - "@rolldown/binding-linux-arm-gnueabihf" "1.2.3" - "@rolldown/binding-linux-arm64-gnu" "1.2.3" - "@rolldown/binding-linux-arm64-musl" "1.2.3" - "@rolldown/binding-linux-ppc64-gnu" "1.2.3" - "@rolldown/binding-linux-s390x-gnu" "1.2.3" - "@rolldown/binding-linux-x64-gnu" "1.2.3" - "@rolldown/binding-linux-x64-musl" "1.2.3" - "@rolldown/binding-openharmony-arm64" "1.2.3" - "@rolldown/binding-win32-arm64-msvc" "1.2.3" - "@rolldown/binding-win32-x64-msvc" "1.2.3" + "@rolldown/binding-android-arm-eabi" "1.2.5" + "@rolldown/binding-android-arm64" "1.2.5" + "@rolldown/binding-darwin-arm64" "1.2.5" + "@rolldown/binding-darwin-x64" "1.2.5" + "@rolldown/binding-freebsd-x64" "1.2.5" + "@rolldown/binding-linux-arm-gnueabihf" "1.2.5" + "@rolldown/binding-linux-arm64-gnu" "1.2.5" + "@rolldown/binding-linux-arm64-musl" "1.2.5" + "@rolldown/binding-linux-ppc64-gnu" "1.2.5" + "@rolldown/binding-linux-s390x-gnu" "1.2.5" + "@rolldown/binding-linux-x64-gnu" "1.2.5" + "@rolldown/binding-linux-x64-musl" "1.2.5" + "@rolldown/binding-openharmony-arm64" "1.2.5" + "@rolldown/binding-win32-arm64-msvc" "1.2.5" + "@rolldown/binding-win32-x64-msvc" "1.2.5" rolldown@1.1.2, rolldown@^1.0.0-rc.15, rolldown@^1.0.0-rc.8: version "1.1.2" From 6dc4a0ca6f54b3a7132dfaf1d36be631d18ff24f Mon Sep 17 00:00:00 2001 From: Anders Chen Date: Wed, 26 Aug 2026 20:02:57 +0000 Subject: [PATCH 3/3] refactor(bundler-plugins): organize Rollup debug ID handling --- packages/bundler-plugins/package.json | 2 +- ...down-debug-id.ts => debug-id-injection.ts} | 50 ++++-- packages/bundler-plugins/src/rollup/index.ts | 50 +++--- .../rollup/{utils.ts => rollup-version.ts} | 7 - .../src/rollup/vite-annotations.ts | 18 +- .../test/rollup/debug-id-injection.test.ts | 100 +++++++++++ .../test/rollup/public-api.test.ts | 157 ------------------ .../test/rollup/rolldown-determinism.test.ts | 4 +- yarn.lock | 2 +- 9 files changed, 174 insertions(+), 216 deletions(-) rename packages/bundler-plugins/src/rollup/{rolldown-debug-id.ts => debug-id-injection.ts} (50%) rename packages/bundler-plugins/src/rollup/{utils.ts => rollup-version.ts} (61%) create mode 100644 packages/bundler-plugins/test/rollup/debug-id-injection.test.ts diff --git a/packages/bundler-plugins/package.json b/packages/bundler-plugins/package.json index 014d09854f1c..baddefef28e7 100644 --- a/packages/bundler-plugins/package.json +++ b/packages/bundler-plugins/package.json @@ -138,7 +138,7 @@ "@types/webpack": "npm:@types/webpack@^4", "premove": "^4.0.0", "rolldown": "1.1.2", - "rolldown-1-2": "npm:rolldown@1.2.5", + "rolldown-1-2-5": "npm:rolldown@1.2.5", "vitest": "^3.2.7", "webpack": "5.104.1" }, diff --git a/packages/bundler-plugins/src/rollup/rolldown-debug-id.ts b/packages/bundler-plugins/src/rollup/debug-id-injection.ts similarity index 50% rename from packages/bundler-plugins/src/rollup/rolldown-debug-id.ts rename to packages/bundler-plugins/src/rollup/debug-id-injection.ts index 26122f1aae09..2b59f3216192 100644 --- a/packages/bundler-plugins/src/rollup/rolldown-debug-id.ts +++ b/packages/bundler-plugins/src/rollup/debug-id-injection.ts @@ -2,50 +2,68 @@ import { stringToUUID } from '../core'; export const ROLLDOWN_DEBUG_ID_PLACEHOLDER = 'SENTRY_DEBUG_ID_PLACEHOLDER_00000000'; -type GeneratedBundle = Record< - string, - { - type?: string; - fileName?: string; - code?: string; - } ->; +type GeneratedChunk = { + type: 'chunk'; + fileName: string; + code: string; +}; + +type GeneratedAsset = { + type: 'asset'; + fileName: string; +}; + +export type GeneratedBundle = Record; const SENTRY_DEBUG_ID_IDENTIFIER = '_sentryDebugIdIdentifier'; const SENTRY_DEBUG_ID_IDENTIFIER_PREFIX = 'sentry-dbid-'; +export function hasExistingDebugID(code: string): boolean { + const chunkStartSnippet = code.slice(0, 6000); + const chunkEndSnippet = code.slice(-500); + + return chunkStartSnippet.includes(SENTRY_DEBUG_ID_IDENTIFIER) || chunkEndSnippet.includes('//# debugId='); +} + +export function getDebugIdForChunk(code: string, isRolldown: boolean): string { + return isRolldown ? ROLLDOWN_DEBUG_ID_PLACEHOLDER : stringToUUID(code); +} + function replaceAt(code: string, start: number, search: string, replacement: string): string { return `${code.slice(0, start)}${replacement}${code.slice(start + search.length)}`; } export function finalizeRolldownDebugIds(bundle: GeneratedBundle): void { - for (const [fileName, chunk] of Object.entries(bundle)) { - if (chunk.type !== 'chunk' || !chunk.code) { + for (const [fileName, output] of Object.entries(bundle)) { + if (output.type !== 'chunk') { continue; } const identifier = `${SENTRY_DEBUG_ID_IDENTIFIER_PREFIX}${ROLLDOWN_DEBUG_ID_PLACEHOLDER}`; - const identifierPropertyStart = chunk.code.indexOf(SENTRY_DEBUG_ID_IDENTIFIER); - const identifierStart = chunk.code.indexOf(identifier, identifierPropertyStart + SENTRY_DEBUG_ID_IDENTIFIER.length); + const identifierPropertyStart = output.code.indexOf(SENTRY_DEBUG_ID_IDENTIFIER); + const identifierStart = output.code.indexOf( + identifier, + identifierPropertyStart + SENTRY_DEBUG_ID_IDENTIFIER.length, + ); if (identifierStart === -1) { continue; } const identifierPlaceholderStart = identifierStart + SENTRY_DEBUG_ID_IDENTIFIER_PREFIX.length; - const debugIdsPlaceholderStart = chunk.code.lastIndexOf(ROLLDOWN_DEBUG_ID_PLACEHOLDER, identifierStart - 1); + const debugIdsPlaceholderStart = output.code.lastIndexOf(ROLLDOWN_DEBUG_ID_PLACEHOLDER, identifierStart - 1); if (debugIdsPlaceholderStart === -1) { throw new Error(`Failed to locate the Sentry debug ID placeholder for chunk \`${fileName}\`.`); } // Including the final filename disambiguates otherwise identical chunks. The fixed-width replacement deliberately // happens after Rolldown computes [hash], so the emitted filename represents the placeholder-bearing chunk. - const debugId = stringToUUID(JSON.stringify([chunk.fileName ?? fileName, chunk.code])); + const debugId = stringToUUID(JSON.stringify([output.fileName, output.code])); const codeWithIdentifier = replaceAt( - chunk.code, + output.code, identifierPlaceholderStart, ROLLDOWN_DEBUG_ID_PLACEHOLDER, debugId, ); - chunk.code = replaceAt(codeWithIdentifier, debugIdsPlaceholderStart, ROLLDOWN_DEBUG_ID_PLACEHOLDER, debugId); + output.code = replaceAt(codeWithIdentifier, debugIdsPlaceholderStart, ROLLDOWN_DEBUG_ID_PLACEHOLDER, debugId); } } diff --git a/packages/bundler-plugins/src/rollup/index.ts b/packages/bundler-plugins/src/rollup/index.ts index 8ff44337bd83..13bd8545eae5 100644 --- a/packages/bundler-plugins/src/rollup/index.ts +++ b/packages/bundler-plugins/src/rollup/index.ts @@ -6,7 +6,6 @@ import { isJsFile, shouldSkipCodeInjection, getDebugIdSnippet, - stringToUUID, COMMENT_USE_STRICT_REGEX, createDebugIdUploadFunction, globFiles, @@ -18,9 +17,14 @@ import type { ComponentAnnotationTransformMeta } from '../core/component-annotat import type { SourceMap } from 'magic-string'; import MagicString from 'magic-string'; import * as path from 'node:path'; -import { finalizeRolldownDebugIds, ROLLDOWN_DEBUG_ID_PLACEHOLDER } from './rolldown-debug-id'; -import { getRollupMajorVersion, hasExistingDebugID } from './utils'; -import { getViteParseAstAsync, type ViteAnnotationHooks } from './vite-annotations'; +import { + finalizeRolldownDebugIds, + getDebugIdForChunk, + hasExistingDebugID, + type GeneratedBundle, +} from './debug-id-injection'; +import { getRollupMajorVersion } from './rollup-version'; +import { createViteAnnotationHooks } from './vite-annotations'; // The subset of Rollup's `TransformResult` that this plugin's `transform` // hook actually returns. Defined locally instead of imported from `rollup` @@ -105,25 +109,7 @@ export function _rollupPluginInternal( buildTool === 'vite' && buildToolMajorVersion === '8' && !options.reactComponentAnnotation?._experimentalInjectIntoHtml - ? (() => { - let viteAnnotationHooksPromise: Promise | undefined; - - return { - transform(code: string, id: string, meta?: ComponentAnnotationTransformMeta) { - if (!viteAnnotationHooksPromise) { - viteAnnotationHooksPromise = import('../core/component-annotation-vite').then( - ({ createViteComponentNameAnnotateHooks }) => - createViteComponentNameAnnotateHooks( - options.reactComponentAnnotation?.ignoredComponents || [], - getViteParseAstAsync, - ), - ); - } - - return viteAnnotationHooksPromise.then(hooks => hooks.transform(code, id, meta)); - }, - }; - })() + ? createViteAnnotationHooks(options.reactComponentAnnotation?.ignoredComponents || []) : undefined; const transformReplace = Object.keys(replacementValues).length > 0; @@ -191,9 +177,7 @@ export function _rollupPluginInternal( const injectCode = staticInjectionCode.clone(); if (sourcemapsEnabled && !hasExistingDebugID(code)) { - // Rolldown's renderChunk code contains temporary hash placeholders whose values can vary between builds. - // The fixed-width placeholder is replaced after Rolldown resolves them, without shifting source map positions. - const debugId = this?.meta?.rolldownVersion ? ROLLDOWN_DEBUG_ID_PLACEHOLDER : stringToUUID(code); + const debugId = getDebugIdForChunk(code, !!this?.meta?.rolldownVersion); injectCode.append(getDebugIdSnippet(debugId)); } @@ -233,7 +217,7 @@ export function _rollupPluginInternal( function generateBundle( this: GenerateBundlePluginContext | undefined, _outputOptions: unknown, - bundle: Parameters[0], + bundle: GeneratedBundle, ): void { if (!this?.meta?.rolldownVersion) { return; @@ -278,10 +262,14 @@ export function _rollupPluginInternal( } const name = `sentry-${buildTool}-plugin`; - const generateBundleHook = - buildTool === 'vite' && buildToolMajorVersion === '8' - ? { order: 'post' as const, handler: generateBundle } - : generateBundle; + function createGenerateBundleHook() { + if (buildTool === 'vite' && buildToolMajorVersion === '8') { + return { order: 'post' as const, handler: generateBundle }; + } + + return generateBundle; + } + const generateBundleHook = createGenerateBundleHook(); if (shouldTransform) { const transformHook = diff --git a/packages/bundler-plugins/src/rollup/utils.ts b/packages/bundler-plugins/src/rollup/rollup-version.ts similarity index 61% rename from packages/bundler-plugins/src/rollup/utils.ts rename to packages/bundler-plugins/src/rollup/rollup-version.ts index e2c93ef1855f..7e04a1468ed5 100644 --- a/packages/bundler-plugins/src/rollup/utils.ts +++ b/packages/bundler-plugins/src/rollup/rollup-version.ts @@ -1,12 +1,5 @@ import { createRequire } from 'node:module'; -export function hasExistingDebugID(code: string): boolean { - const chunkStartSnippet = code.slice(0, 6000); - const chunkEndSnippet = code.slice(-500); - - return chunkStartSnippet.includes('_sentryDebugIdIdentifier') || chunkEndSnippet.includes('//# debugId='); -} - export function getRollupMajorVersion(): string | undefined { try { // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/packages/bundler-plugins/src/rollup/vite-annotations.ts b/packages/bundler-plugins/src/rollup/vite-annotations.ts index ca97bed9cfba..6ae8d28cdc36 100644 --- a/packages/bundler-plugins/src/rollup/vite-annotations.ts +++ b/packages/bundler-plugins/src/rollup/vite-annotations.ts @@ -10,7 +10,7 @@ type ViteModule = { type ViteParseAstAsync = NonNullable; -export type ViteAnnotationHooks = { +type ViteAnnotationHooks = { transform( code: string, id: string, @@ -45,3 +45,19 @@ export function getViteParseAstAsync(): Promise { return viteParseAstAsyncPromise; } + +export function createViteAnnotationHooks(ignoredComponents: string[]): ViteAnnotationHooks { + let hooksPromise: Promise | undefined; + + return { + transform(code, id, meta) { + if (!hooksPromise) { + hooksPromise = import('../core/component-annotation-vite').then(({ createViteComponentNameAnnotateHooks }) => + createViteComponentNameAnnotateHooks(ignoredComponents, getViteParseAstAsync), + ); + } + + return hooksPromise.then(hooks => hooks.transform(code, id, meta)); + }, + }; +} diff --git a/packages/bundler-plugins/test/rollup/debug-id-injection.test.ts b/packages/bundler-plugins/test/rollup/debug-id-injection.test.ts new file mode 100644 index 000000000000..5dcfd2682676 --- /dev/null +++ b/packages/bundler-plugins/test/rollup/debug-id-injection.test.ts @@ -0,0 +1,100 @@ +import { describe, expect, it } from 'vitest'; +import { + finalizeRolldownDebugIds, + getDebugIdForChunk, + hasExistingDebugID, + ROLLDOWN_DEBUG_ID_PLACEHOLDER, + type GeneratedBundle, +} from '../../src/rollup/debug-id-injection'; + +const UUID_PATTERN = /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/g; + +function provisionalCode(userCode = 'console.log("test");'): string { + return [ + `globalThis._sentryDebugIds[stack]="${ROLLDOWN_DEBUG_ID_PLACEHOLDER}";`, + `globalThis._sentryDebugIdIdentifier="sentry-dbid-${ROLLDOWN_DEBUG_ID_PLACEHOLDER}";`, + userCode, + ].join(''); +} + +function finalize(code: string, fileName = 'bundle.js'): string { + const bundle: GeneratedBundle = { + [fileName]: { type: 'chunk', fileName, code }, + }; + + finalizeRolldownDebugIds(bundle); + const output = bundle[fileName]; + expect(output?.type).toBe('chunk'); + + return output?.type === 'chunk' ? output.code : ''; +} + +function extractDebugId(code: string): string { + const debugIds = code.match(UUID_PATTERN); + expect(debugIds).toHaveLength(2); + expect(new Set(debugIds)).toHaveLength(1); + + return debugIds?.[0] ?? ''; +} + +describe('debug ID injection', () => { + it('uses a placeholder for Rolldown and a deterministic UUID for Rollup', () => { + expect(getDebugIdForChunk('code', true)).toBe(ROLLDOWN_DEBUG_ID_PLACEHOLDER); + expect(getDebugIdForChunk('code', false)).toMatch(UUID_PATTERN); + expect(getDebugIdForChunk('code', false)).toBe(getDebugIdForChunk('code', false)); + }); + + it('detects existing inline and comment debug IDs at chunk boundaries', () => { + expect(hasExistingDebugID('globalThis._sentryDebugIdIdentifier="sentry-dbid-existing";')).toBe(true); + expect(hasExistingDebugID('console.log("test");\n//# debugId=existing')).toBe(true); + expect(hasExistingDebugID('console.log("test");')).toBe(false); + }); + + it('generates stable IDs from finalized code and filenames', () => { + const firstBuild = finalize(provisionalCode()); + const secondBuild = finalize(provisionalCode()); + + expect(extractDebugId(firstBuild)).toBe(extractDebugId(secondBuild)); + }); + + it('changes the ID when code or the filename changes', () => { + const baseline = extractDebugId(finalize(provisionalCode('first'))); + + expect(extractDebugId(finalize(provisionalCode('second')))).not.toBe(baseline); + expect(extractDebugId(finalize(provisionalCode('first'), 'other.js'))).not.toBe(baseline); + }); + + it('does not replace placeholder-shaped user strings', () => { + const userCode = `console.log("${ROLLDOWN_DEBUG_ID_PLACEHOLDER}");`; + + expect(finalize(provisionalCode(userCode))).toContain(userCode); + }); + + it('does not replace marker-shaped strings before the injected identifier', () => { + const userCode = `// sentry-dbid-${ROLLDOWN_DEBUG_ID_PLACEHOLDER}\n`; + const finalizedCode = finalize(`${userCode}${provisionalCode()}`); + + expect(finalizedCode).toContain(userCode); + expect(extractDebugId(finalizedCode)).not.toBe(''); + }); + + it('fails when the injected placeholder is incomplete', () => { + const code = `globalThis._sentryDebugIdIdentifier="sentry-dbid-${ROLLDOWN_DEBUG_ID_PLACEHOLDER}";`; + + expect(() => finalize(code)).toThrow('Failed to locate the Sentry debug ID placeholder for chunk `bundle.js`.'); + }); + + it('ignores non-chunk assets and chunks without provisional IDs', () => { + const bundle: GeneratedBundle = { + 'asset.js': { type: 'asset', fileName: 'asset.js' }, + 'chunk.js': { type: 'chunk', fileName: 'chunk.js', code: 'console.log("test");' }, + }; + + finalizeRolldownDebugIds(bundle); + + expect(bundle).toEqual({ + 'asset.js': { type: 'asset', fileName: 'asset.js' }, + 'chunk.js': { type: 'chunk', fileName: 'chunk.js', code: 'console.log("test");' }, + }); + }); +}); diff --git a/packages/bundler-plugins/test/rollup/public-api.test.ts b/packages/bundler-plugins/test/rollup/public-api.test.ts index 630bbd2b15c6..1ddb07b58d31 100644 --- a/packages/bundler-plugins/test/rollup/public-api.test.ts +++ b/packages/bundler-plugins/test/rollup/public-api.test.ts @@ -314,160 +314,3 @@ bootstrap();`; }); }); }); - -describe('Rolldown debug ID finalization', () => { - const [plugin] = sentryRollupPlugin({ - release: { inject: false }, - sourcemaps: { disable: 'disable-upload' }, - }) as [Plugin]; - const rolldownContext = { meta: { rolldownVersion: '1.2.5' } }; - const rollupContext: { meta: { rolldownVersion?: string } } = { meta: {} }; - const renderChunk = plugin.renderChunk as unknown as ( - this: typeof rolldownContext, - code: string, - chunkInfo: { fileName: string }, - ) => { code: string } | null; - const generateBundle = plugin.generateBundle as ( - this: typeof rollupContext, - outputOptions: unknown, - bundle: Record, - ) => void; - - function extractDebugId(code: string): string { - const debugIds = code.match(/[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/g); - expect(debugIds).toHaveLength(2); - expect(new Set(debugIds)).toHaveLength(1); - - return debugIds?.[0] ?? ''; - } - - function renderAndFinalize(code: string, fileName: string): string { - const result = renderChunk.call(rolldownContext, code, { fileName }); - expect(result).not.toBeNull(); - - const bundle = { - [fileName]: { - type: 'chunk' as const, - fileName, - code: result?.code || '', - }, - }; - generateBundle.call(rolldownContext, {}, bundle); - - return bundle[fileName]?.code ?? ''; - } - - it('defers the debug ID until Rolldown has finalized the chunk', () => { - const result = renderChunk.call(rolldownContext, 'console.log("test");', { - fileName: 'bundle.js', - }); - - expect(result?.code).toContain('SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); - - const finalizedCode = renderAndFinalize('console.log("test");', 'bundle.js'); - expect(finalizedCode).not.toContain('SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); - expect(extractDebugId(finalizedCode)).not.toBe(''); - }); - - it('generates stable IDs from finalized code and filenames', () => { - const firstBuild = renderAndFinalize('console.log("test");', 'bundle.js'); - const secondBuild = renderAndFinalize('console.log("test");', 'bundle.js'); - - expect(extractDebugId(firstBuild)).toBe(extractDebugId(secondBuild)); - }); - - it('changes the ID when finalized code changes', () => { - const firstBuild = renderAndFinalize('console.log("first");', 'bundle.js'); - const secondBuild = renderAndFinalize('console.log("second");', 'bundle.js'); - - expect(extractDebugId(firstBuild)).not.toBe(extractDebugId(secondBuild)); - }); - - it('assigns different IDs to identical chunks with different filenames', () => { - const firstChunk = renderAndFinalize('console.log("test");', 'first.js'); - const secondChunk = renderAndFinalize('console.log("test");', 'second.js'); - - expect(extractDebugId(firstChunk)).not.toBe(extractDebugId(secondChunk)); - }); - - it('does not replace placeholder-shaped strings in user code', () => { - const userCode = 'console.log("SENTRY_DEBUG_ID_PLACEHOLDER_00000000");'; - const finalizedCode = renderAndFinalize(userCode, 'bundle.js'); - - expect(finalizedCode).toContain(userCode); - }); - - it('does not replace placeholder-shaped strings before the injected snippet', () => { - const userCode = '// SENTRY_DEBUG_ID_PLACEHOLDER_00000000\nconsole.log("test");'; - const finalizedCode = renderAndFinalize(userCode, 'bundle.js'); - - expect(finalizedCode).toContain('// SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); - expect(finalizedCode).toContain('console.log("test");'); - expect(extractDebugId(finalizedCode)).not.toBe(''); - }); - - it('does not replace a marker-shaped string before the injected identifier', () => { - const userCode = '// sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000\nconsole.log("test");'; - const finalizedCode = renderAndFinalize(userCode, 'bundle.js'); - - expect(finalizedCode).toContain('// sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000'); - expect(finalizedCode).toContain('console.log("test");'); - expect(extractDebugId(finalizedCode)).not.toBe(''); - }); - - it('does not finalize marker-shaped Rollup user code', () => { - const code = 'console.log("sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000");'; - const bundle = { - 'bundle.js': { type: 'chunk' as const, fileName: 'bundle.js', code }, - }; - - generateBundle.call(rollupContext, {}, bundle); - - expect(bundle['bundle.js'].code).toBe(code); - }); - - it('fails when the injected debug ID placeholder is incomplete', () => { - const bundle = { - 'bundle.js': { - type: 'chunk' as const, - fileName: 'bundle.js', - code: 'globalThis._sentryDebugIdIdentifier="sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000";', - }, - }; - - expect(() => generateBundle.call(rolldownContext, {}, bundle)).toThrow( - 'Failed to locate the Sentry debug ID placeholder for chunk `bundle.js`.', - ); - }); - - it('ignores non-chunk assets', () => { - const code = 'sentry-dbid-SENTRY_DEBUG_ID_PLACEHOLDER_00000000'; - const bundle = { - 'asset.js': { type: 'asset' as const, fileName: 'asset.js', code }, - }; - - generateBundle.call(rolldownContext, {}, bundle as never); - - expect(bundle['asset.js'].code).toBe(code); - }); - - it('leaves existing debug IDs untouched in Rolldown', () => { - const code = 'globalThis._sentryDebugIdIdentifier="sentry-dbid-f6ccd6f4-7ea0-4854-8384-1c9f8340af81";'; - - expect(renderChunk.call(rolldownContext, code, { fileName: 'bundle.js' })).toBeNull(); - }); - - it('does not inject a placeholder when sourcemaps are disabled', () => { - const [disabledPlugin] = sentryRollupPlugin({ - release: { inject: false }, - sourcemaps: { disable: true }, - }) as [Plugin]; - const disabledRenderChunk = disabledPlugin.renderChunk as unknown as typeof renderChunk; - - expect( - disabledRenderChunk.call(rolldownContext, 'console.log("test");', { - fileName: 'bundle.js', - }), - ).toBeNull(); - }); -}); diff --git a/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts b/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts index 9dc8ef4b3f2b..cf055cc6f9e6 100644 --- a/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts +++ b/packages/bundler-plugins/test/rollup/rolldown-determinism.test.ts @@ -1,5 +1,5 @@ import { rolldown as rolldown112 } from 'rolldown'; -import { rolldown as rolldown123 } from 'rolldown-1-2'; +import { rolldown as rolldown125 } from 'rolldown-1-2-5'; import { describe, expect, it } from 'vitest'; import { sentryRollupPlugin } from '../../src/rollup'; @@ -48,7 +48,7 @@ async function createBuild(version: RolldownVersion, includeUnrelatedEntry: bool ], }; - return version === '1.1.2' ? rolldown112(options) : rolldown123(options); + return version === '1.1.2' ? rolldown112(options) : rolldown125(options); } async function build( diff --git a/yarn.lock b/yarn.lock index 2dfabe9a5d81..cca22bce73df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24352,7 +24352,7 @@ roarr@^7.0.4: safe-stable-stringify "^2.4.1" semver-compare "^1.0.0" -"rolldown-1-2@npm:rolldown@1.2.5": +"rolldown-1-2-5@npm:rolldown@1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.5.tgz#1f504a7d05260a769e617d950410bbb051498c50" integrity sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==