From 6d6c734d221640da1eb77893492959e62197cc67 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 29 Apr 2025 11:25:59 +0200 Subject: [PATCH 1/5] pass build options correctly --- .../src/vite/makeCustomSentryVitePlugins.ts | 18 +++++- .../vite/makeCustomSentryVitePlugins.test.ts | 63 +++++++++++++++++++ .../react-router/test/vite/plugin.test.ts | 24 +++++++ 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 packages/react-router/test/vite/makeCustomSentryVitePlugins.test.ts diff --git a/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts b/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts index 7702938f0455..7bba1bf985f1 100644 --- a/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts +++ b/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts @@ -6,8 +6,16 @@ import type { SentryReactRouterBuildOptions } from './types'; * Create a custom subset of sentry's vite plugins */ export async function makeCustomSentryVitePlugins(options: SentryReactRouterBuildOptions): Promise { - const { debug, unstable_sentryVitePluginOptions, bundleSizeOptimizations, authToken, org, project, telemetry } = - options; + const { + debug, + unstable_sentryVitePluginOptions, + bundleSizeOptimizations, + authToken, + org, + project, + telemetry, + release, + } = options; const sentryVitePlugins = sentryVitePlugin({ authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN, @@ -20,10 +28,16 @@ export async function makeCustomSentryVitePlugins(options: SentryReactRouterBuil telemetry: { metaFramework: 'react-router', }, + ...unstable_sentryVitePluginOptions?._metaOptions, + }, + release: { + ...release, + ...unstable_sentryVitePluginOptions?.release, }, // will be handled in buildEnd hook sourcemaps: { disable: true, + ...unstable_sentryVitePluginOptions?.sourcemaps, }, ...unstable_sentryVitePluginOptions, }) as Plugin[]; diff --git a/packages/react-router/test/vite/makeCustomSentryVitePlugins.test.ts b/packages/react-router/test/vite/makeCustomSentryVitePlugins.test.ts new file mode 100644 index 000000000000..04576076a561 --- /dev/null +++ b/packages/react-router/test/vite/makeCustomSentryVitePlugins.test.ts @@ -0,0 +1,63 @@ +import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { describe, expect, it, vi } from 'vitest'; +import { makeCustomSentryVitePlugins } from '../../src/vite/makeCustomSentryVitePlugins'; + +vi.mock('@sentry/vite-plugin', () => ({ + sentryVitePlugin: vi + .fn() + .mockReturnValue([ + { name: 'sentry-telemetry-plugin' }, + { name: 'sentry-vite-release-injection-plugin' }, + { name: 'other-plugin' }, + ]), +})); + +describe('makeCustomSentryVitePlugins', () => { + it('should pass release configuration to sentryVitePlugin', async () => { + const options = { + release: { + name: 'test-release', + }, + }; + + await makeCustomSentryVitePlugins(options); + + expect(sentryVitePlugin).toHaveBeenCalledWith( + expect.objectContaining({ + release: { + name: 'test-release', + }, + }), + ); + }); + + it('should merge release configuration with unstable_sentryVitePluginOptions', async () => { + const options = { + release: { + name: 'test-release', + }, + unstable_sentryVitePluginOptions: { + release: { + name: 'unstable-release', + }, + }, + }; + + await makeCustomSentryVitePlugins(options); + + expect(sentryVitePlugin).toHaveBeenCalledWith( + expect.objectContaining({ + release: { + name: 'test-release', + }, + }), + ); + }); + + it('should only return telemetry and release injection plugins', async () => { + const plugins = await makeCustomSentryVitePlugins({}); + expect(plugins).toHaveLength(2); + expect(plugins?.[0]?.name).toBe('sentry-telemetry-plugin'); + expect(plugins?.[1]?.name).toBe('sentry-vite-release-injection-plugin'); + }); +}); diff --git a/packages/react-router/test/vite/plugin.test.ts b/packages/react-router/test/vite/plugin.test.ts index 67ab338538da..db23a04ec2cd 100644 --- a/packages/react-router/test/vite/plugin.test.ts +++ b/packages/react-router/test/vite/plugin.test.ts @@ -1,5 +1,6 @@ import type { Plugin } from 'vite'; import { describe, expect, it, vi } from 'vitest'; +import { makeCustomSentryVitePlugins } from '../../src/vite/makeCustomSentryVitePlugins'; import { sentryReactRouter } from '../../src/vite/plugin'; vi.spyOn(console, 'log').mockImplementation(() => { @@ -9,6 +10,12 @@ vi.spyOn(console, 'warn').mockImplementation(() => { /* noop */ }); +vi.mock('../../src/vite/makeCustomSentryVitePlugins', () => ({ + makeCustomSentryVitePlugins: vi.fn().mockImplementation(async _options => { + return [{ name: 'sentry-telemetry-plugin' }, { name: 'sentry-vite-release-injection-plugin' }]; + }), +})); + async function getSentryReactRouterVitePlugins(options?: Parameters[0]): Promise { return sentryReactRouter( { @@ -35,4 +42,21 @@ describe('sentryReactRouter()', () => { 'sentry-vite-release-injection-plugin', ]); }); + + it('passes release configuration to plugins', async () => { + const releaseName = 'test-release'; + await getSentryReactRouterVitePlugins({ + release: { + name: releaseName, + }, + }); + + expect(makeCustomSentryVitePlugins).toHaveBeenCalledWith( + expect.objectContaining({ + release: { + name: releaseName, + }, + }), + ); + }); }); From 3778700b008e0230b27b6cd53d65a2dac10826d9 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 29 Apr 2025 11:43:35 +0200 Subject: [PATCH 2/5] extends options in handleOnBuildEnd --- .../src/vite/buildEnd/handleOnBuildEnd.ts | 10 +- .../vite/buildEnd/handleOnBuildEnd.test.ts | 82 ++++++++---- .../react-router/test/vite/plugin.test.ts | 119 +++++++++++------- 3 files changed, 143 insertions(+), 68 deletions(-) diff --git a/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts b/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts index d34b5394945e..1b939b1eed8c 100644 --- a/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts +++ b/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts @@ -21,6 +21,8 @@ function getSentryConfig(viteConfig: unknown): SentryReactRouterBuildOptions { * and optionally deletes the source map files after upload. */ export const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteConfig }) => { + const sentryConfig = getSentryConfig(viteConfig); + const { authToken, org, @@ -29,7 +31,13 @@ export const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteCo sourceMapsUploadOptions = { enabled: true }, debug = false, unstable_sentryVitePluginOptions, - } = getSentryConfig(viteConfig); + }: SentryReactRouterBuildOptions = { + ...sentryConfig, + release: { + ...sentryConfig.unstable_sentryVitePluginOptions?.release, + ...sentryConfig.release, + }, + }; const cliInstance = new SentryCli(null, { authToken, diff --git a/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts b/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts index 2b53eb267b15..21d8616e5e44 100644 --- a/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts +++ b/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts @@ -1,10 +1,11 @@ import SentryCli from '@sentry/cli'; import * as fs from 'fs'; import { glob } from 'glob'; +import type { ResolvedConfig } from 'vite'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { sentryOnBuildEnd } from '../../../src/vite/buildEnd/handleOnBuildEnd'; +import type { SentryReactRouterBuildOptions } from '../../../src/vite/types'; -// Mock dependencies vi.mock('@sentry/cli'); vi.mock('fs', () => ({ promises: { @@ -13,6 +14,10 @@ vi.mock('fs', () => ({ })); vi.mock('glob'); +type TestConfig = ResolvedConfig & { + sentryConfig: SentryReactRouterBuildOptions; +}; + describe('sentryOnBuildEnd', () => { const mockSentryCliInstance = { releases: { @@ -47,7 +52,7 @@ describe('sentryOnBuildEnd', () => { project: 'test-project', debug: false, }, - }, + } as unknown as TestConfig, }; beforeEach(() => { @@ -73,7 +78,52 @@ describe('sentryOnBuildEnd', () => { name: 'v1.0.0', }, }, - }, + } as unknown as TestConfig, + }; + + await sentryOnBuildEnd(config); + + expect(mockSentryCliInstance.releases.new).toHaveBeenCalledWith('v1.0.0'); + }); + + it('should create a new Sentry release when release name is provided in unstable_sentryVitePluginOptions', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + unstable_sentryVitePluginOptions: { + release: { + name: 'v1.0.0-unstable', + }, + }, + }, + } as unknown as TestConfig, + }; + + await sentryOnBuildEnd(config); + + expect(mockSentryCliInstance.releases.new).toHaveBeenCalledWith('v1.0.0-unstable'); + }); + + it('should prioritize release name from main config over unstable_sentryVitePluginOptions', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + release: { + name: 'v1.0.0', + }, + unstable_sentryVitePluginOptions: { + release: { + name: 'v1.0.0-unstable', + }, + }, + }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -92,7 +142,7 @@ describe('sentryOnBuildEnd', () => { enabled: true, }, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -113,7 +163,7 @@ describe('sentryOnBuildEnd', () => { enabled: false, }, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -141,7 +191,7 @@ describe('sentryOnBuildEnd', () => { filesToDeleteAfterUpload: '/custom/**/*.map', }, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -166,7 +216,7 @@ describe('sentryOnBuildEnd', () => { name: 'v1.0.0', }, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -186,7 +236,7 @@ describe('sentryOnBuildEnd', () => { enabled: true, }, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -225,7 +275,7 @@ describe('sentryOnBuildEnd', () => { ...defaultConfig.viteConfig.sentryConfig, debug: true, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); @@ -252,21 +302,11 @@ describe('sentryOnBuildEnd', () => { ...defaultConfig.viteConfig.sentryConfig, unstable_sentryVitePluginOptions: customOptions, }, - }, + } as unknown as TestConfig, }; await sentryOnBuildEnd(config); - // Verify SentryCli was constructed with the correct options - expect(SentryCli).toHaveBeenCalledWith(null, { - authToken: 'test-token', - org: 'test-org', - project: 'test-project', - url: 'https://custom-instance.ejemplo.es', - headers: { - 'X-Custom-Header': 'test-value', - }, - timeout: 30000, - }); + expect(SentryCli).toHaveBeenCalledWith(null, expect.objectContaining(customOptions)); }); }); diff --git a/packages/react-router/test/vite/plugin.test.ts b/packages/react-router/test/vite/plugin.test.ts index db23a04ec2cd..88d3701646f2 100644 --- a/packages/react-router/test/vite/plugin.test.ts +++ b/packages/react-router/test/vite/plugin.test.ts @@ -1,6 +1,6 @@ -import type { Plugin } from 'vite'; -import { describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { makeCustomSentryVitePlugins } from '../../src/vite/makeCustomSentryVitePlugins'; +import { makeEnableSourceMapsPlugin } from '../../src/vite/makeEnableSourceMapsPlugin'; import { sentryReactRouter } from '../../src/vite/plugin'; vi.spyOn(console, 'log').mockImplementation(() => { @@ -10,53 +10,80 @@ vi.spyOn(console, 'warn').mockImplementation(() => { /* noop */ }); -vi.mock('../../src/vite/makeCustomSentryVitePlugins', () => ({ - makeCustomSentryVitePlugins: vi.fn().mockImplementation(async _options => { - return [{ name: 'sentry-telemetry-plugin' }, { name: 'sentry-vite-release-injection-plugin' }]; - }), -})); - -async function getSentryReactRouterVitePlugins(options?: Parameters[0]): Promise { - return sentryReactRouter( - { - project: 'project', - org: 'org', - authToken: 'token', - ...options, - }, - { - command: 'build', - mode: 'production', - }, - ); -} - -describe('sentryReactRouter()', () => { - it('returns an array of vite plugins', async () => { - const plugins = await getSentryReactRouterVitePlugins(); - expect(plugins).toBeDefined(); - const names = plugins.map(plugin => plugin.name); - expect(names).toEqual([ - 'sentry-react-router-update-source-map-setting', - 'sentry-telemetry-plugin', - 'sentry-vite-release-injection-plugin', - ]); +vi.mock('../../src/vite/makeCustomSentryVitePlugins'); +vi.mock('../../src/vite/makeEnableSourceMapsPlugin'); + +describe('sentryReactRouter', () => { + const mockPlugins = [{ name: 'test-plugin' }]; + const mockSourceMapsPlugin = { name: 'source-maps-plugin' }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(makeCustomSentryVitePlugins).mockResolvedValue(mockPlugins); + vi.mocked(makeEnableSourceMapsPlugin).mockReturnValue(mockSourceMapsPlugin); + }); + + afterEach(() => { + vi.resetModules(); + }); + + it('should return an empty array 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(makeCustomSentryVitePlugins).not.toHaveBeenCalled(); + expect(makeEnableSourceMapsPlugin).not.toHaveBeenCalled(); + + process.env.NODE_ENV = originalNodeEnv; + }); + + it('should return an empty array when not in build mode', async () => { + const result = await sentryReactRouter({}, { command: 'serve', mode: 'production' }); + + expect(result).toEqual([]); + expect(makeCustomSentryVitePlugins).not.toHaveBeenCalled(); + expect(makeEnableSourceMapsPlugin).not.toHaveBeenCalled(); }); - it('passes release configuration to plugins', async () => { - const releaseName = 'test-release'; - await getSentryReactRouterVitePlugins({ + it('should return an empty array when in development mode', async () => { + const result = await sentryReactRouter({}, { command: 'build', mode: 'development' }); + + expect(result).toEqual([]); + expect(makeCustomSentryVitePlugins).not.toHaveBeenCalled(); + expect(makeEnableSourceMapsPlugin).not.toHaveBeenCalled(); + }); + + it('should return 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(makeCustomSentryVitePlugins).toHaveBeenCalledWith({}); + expect(makeEnableSourceMapsPlugin).toHaveBeenCalledWith({}); + + process.env.NODE_ENV = originalNodeEnv; + }); + + it('should pass release configuration to plugins', async () => { + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + + const options = { release: { - name: releaseName, + name: 'v1.0.0', }, - }); - - expect(makeCustomSentryVitePlugins).toHaveBeenCalledWith( - expect.objectContaining({ - release: { - name: releaseName, - }, - }), - ); + }; + + await sentryReactRouter(options, { command: 'build', mode: 'production' }); + + expect(makeCustomSentryVitePlugins).toHaveBeenCalledWith(options); + expect(makeEnableSourceMapsPlugin).toHaveBeenCalledWith(options); + + process.env.NODE_ENV = originalNodeEnv; }); }); From fa2984f73d75019a7a55bafef09705c09e088ce4 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 29 Apr 2025 11:45:03 +0200 Subject: [PATCH 3/5] . --- packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts b/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts index 1b939b1eed8c..1836be2b0734 100644 --- a/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts +++ b/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts @@ -32,6 +32,7 @@ export const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteCo debug = false, unstable_sentryVitePluginOptions, }: SentryReactRouterBuildOptions = { + ...sentryConfig.unstable_sentryVitePluginOptions, ...sentryConfig, release: { ...sentryConfig.unstable_sentryVitePluginOptions?.release, From 7dcacd3704a7d0fc29e52f94bf648042dd11f8ab Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 29 Apr 2025 11:46:45 +0200 Subject: [PATCH 4/5] beep --- packages/react-router/src/vite/makeCustomSentryVitePlugins.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts b/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts index 7bba1bf985f1..41580f4a7c7f 100644 --- a/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts +++ b/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts @@ -31,8 +31,8 @@ export async function makeCustomSentryVitePlugins(options: SentryReactRouterBuil ...unstable_sentryVitePluginOptions?._metaOptions, }, release: { - ...release, ...unstable_sentryVitePluginOptions?.release, + ...release, }, // will be handled in buildEnd hook sourcemaps: { From b0e1857257273d678d4555f611c2483311ba94ee Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 29 Apr 2025 12:48:44 +0200 Subject: [PATCH 5/5] weird lint issue? --- packages/core/src/mcp-server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/mcp-server.ts b/packages/core/src/mcp-server.ts index 56c53010b58a..bd3d83795d1c 100644 --- a/packages/core/src/mcp-server.ts +++ b/packages/core/src/mcp-server.ts @@ -6,8 +6,8 @@ import { } from './semanticAttributes'; import { startSpan, withActiveSpan } from './tracing'; import type { Span } from './types-hoist/span'; -import { logger } from './utils-hoist/logger'; import { getActiveSpan } from './utils/spanUtils'; +import { logger } from './utils-hoist/logger'; interface MCPTransport { // The first argument is a JSON RPC message