diff --git a/packages/react-router/src/vite/index.ts b/packages/react-router/src/vite/index.ts index fbe4736dbcfb..5f5b6266015a 100644 --- a/packages/react-router/src/vite/index.ts +++ b/packages/react-router/src/vite/index.ts @@ -1,3 +1,4 @@ export { sentryReactRouter } from './plugin'; export { sentryOnBuildEnd } from './buildEnd/handleOnBuildEnd'; export type { SentryReactRouterBuildOptions } from './types'; +export { makeConfigInjectorPlugin } from './makeConfigInjectorPlugin'; diff --git a/packages/react-router/src/vite/makeConfigInjectorPlugin.ts b/packages/react-router/src/vite/makeConfigInjectorPlugin.ts new file mode 100644 index 000000000000..43948c8d4986 --- /dev/null +++ b/packages/react-router/src/vite/makeConfigInjectorPlugin.ts @@ -0,0 +1,23 @@ +import { type Plugin } from 'vite'; +import type { SentryReactRouterBuildOptions } from './types'; + +/** + * Creates a Vite plugin that injects the Sentry options into the global Vite config. + * This ensures the sentryConfig is available to other components that need access to it, + * like the buildEnd hook. + * + * @param options - Configuration options for the Sentry Vite plugin + * @returns A Vite plugin that injects sentryConfig into the global config + */ +export function makeConfigInjectorPlugin(options: SentryReactRouterBuildOptions): Plugin { + return { + name: 'sentry-react-router-config-injector', + enforce: 'pre', + config(config) { + return { + ...config, + sentryConfig: options, + }; + }, + }; +} diff --git a/packages/react-router/src/vite/plugin.ts b/packages/react-router/src/vite/plugin.ts index c8cdad32ee92..98405771ee1b 100644 --- a/packages/react-router/src/vite/plugin.ts +++ b/packages/react-router/src/vite/plugin.ts @@ -1,5 +1,6 @@ import type { ConfigEnv } from 'vite'; import { type Plugin } from 'vite'; +import { makeConfigInjectorPlugin } from './makeConfigInjectorPlugin'; import { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins'; import { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin'; import type { SentryReactRouterBuildOptions } from './types'; @@ -17,6 +18,8 @@ export async function sentryReactRouter( ): Promise { const plugins: Plugin[] = []; + plugins.push(makeConfigInjectorPlugin(options)); + if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') { plugins.push(makeEnableSourceMapsPlugin(options)); plugins.push(...(await makeCustomSentryVitePlugins(options))); diff --git a/packages/react-router/test/vite/plugin.test.ts b/packages/react-router/test/vite/plugin.test.ts index 88d3701646f2..f01254ca8869 100644 --- a/packages/react-router/test/vite/plugin.test.ts +++ b/packages/react-router/test/vite/plugin.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { makeConfigInjectorPlugin } from '../../src/vite/makeConfigInjectorPlugin'; import { makeCustomSentryVitePlugins } from '../../src/vite/makeCustomSentryVitePlugins'; import { makeEnableSourceMapsPlugin } from '../../src/vite/makeEnableSourceMapsPlugin'; import { sentryReactRouter } from '../../src/vite/plugin'; @@ -12,57 +13,61 @@ vi.spyOn(console, 'warn').mockImplementation(() => { vi.mock('../../src/vite/makeCustomSentryVitePlugins'); vi.mock('../../src/vite/makeEnableSourceMapsPlugin'); +vi.mock('../../src/vite/makeConfigInjectorPlugin'); describe('sentryReactRouter', () => { const mockPlugins = [{ name: 'test-plugin' }]; const mockSourceMapsPlugin = { name: 'source-maps-plugin' }; + const mockConfigInjectorPlugin = { name: 'sentry-config-injector' }; beforeEach(() => { vi.clearAllMocks(); vi.mocked(makeCustomSentryVitePlugins).mockResolvedValue(mockPlugins); vi.mocked(makeEnableSourceMapsPlugin).mockReturnValue(mockSourceMapsPlugin); + vi.mocked(makeConfigInjectorPlugin).mockReturnValue(mockConfigInjectorPlugin); }); afterEach(() => { vi.resetModules(); }); - it('should return an empty array in development mode', async () => { + it('should return sentry config injector plugin in development mode', async () => { const originalNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'development'; const result = await sentryReactRouter({}, { command: 'build', mode: 'production' }); - expect(result).toEqual([]); + expect(result).toEqual([mockConfigInjectorPlugin]); expect(makeCustomSentryVitePlugins).not.toHaveBeenCalled(); expect(makeEnableSourceMapsPlugin).not.toHaveBeenCalled(); process.env.NODE_ENV = originalNodeEnv; }); - it('should return an empty array when not in build mode', async () => { + it('should return config injector plugin when not in build mode', async () => { const result = await sentryReactRouter({}, { command: 'serve', mode: 'production' }); - expect(result).toEqual([]); + expect(result).toEqual([mockConfigInjectorPlugin]); expect(makeCustomSentryVitePlugins).not.toHaveBeenCalled(); expect(makeEnableSourceMapsPlugin).not.toHaveBeenCalled(); }); - it('should return an empty array when in development mode', async () => { + it('should return config injector plugin in development build mode', async () => { const result = await sentryReactRouter({}, { command: 'build', mode: 'development' }); - expect(result).toEqual([]); + expect(result).toEqual([mockConfigInjectorPlugin]); expect(makeCustomSentryVitePlugins).not.toHaveBeenCalled(); expect(makeEnableSourceMapsPlugin).not.toHaveBeenCalled(); }); - it('should return plugins in production build mode', async () => { + it('should return all plugins in production build mode', async () => { const originalNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'production'; const result = await sentryReactRouter({}, { command: 'build', mode: 'production' }); - expect(result).toEqual([mockSourceMapsPlugin, ...mockPlugins]); + expect(result).toEqual([mockConfigInjectorPlugin, mockSourceMapsPlugin, ...mockPlugins]); + expect(makeConfigInjectorPlugin).toHaveBeenCalledWith({}); expect(makeCustomSentryVitePlugins).toHaveBeenCalledWith({}); expect(makeEnableSourceMapsPlugin).toHaveBeenCalledWith({}); @@ -81,6 +86,7 @@ describe('sentryReactRouter', () => { await sentryReactRouter(options, { command: 'build', mode: 'production' }); + expect(makeConfigInjectorPlugin).toHaveBeenCalledWith(options); expect(makeCustomSentryVitePlugins).toHaveBeenCalledWith(options); expect(makeEnableSourceMapsPlugin).toHaveBeenCalledWith(options);