Skip to content

fix(react-router): Spread unstable_sentryVitePluginOptions correctly #16156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -29,7 +31,14 @@ export const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteCo
sourceMapsUploadOptions = { enabled: true },
debug = false,
unstable_sentryVitePluginOptions,
} = getSentryConfig(viteConfig);
}: SentryReactRouterBuildOptions = {
...sentryConfig.unstable_sentryVitePluginOptions,
...sentryConfig,
release: {
...sentryConfig.unstable_sentryVitePluginOptions?.release,
...sentryConfig.release,
},
};

const cliInstance = new SentryCli(null, {
authToken,
Expand Down
18 changes: 16 additions & 2 deletions packages/react-router/src/vite/makeCustomSentryVitePlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Plugin[]> {
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,
Expand All @@ -20,10 +28,16 @@ export async function makeCustomSentryVitePlugins(options: SentryReactRouterBuil
telemetry: {
metaFramework: 'react-router',
},
...unstable_sentryVitePluginOptions?._metaOptions,
},
release: {
...unstable_sentryVitePluginOptions?.release,
...release,
},
// will be handled in buildEnd hook
sourcemaps: {
disable: true,
...unstable_sentryVitePluginOptions?.sourcemaps,
},
...unstable_sentryVitePluginOptions,
}) as Plugin[];
Expand Down
82 changes: 61 additions & 21 deletions packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -13,6 +14,10 @@ vi.mock('fs', () => ({
}));
vi.mock('glob');

type TestConfig = ResolvedConfig & {
sentryConfig: SentryReactRouterBuildOptions;
};

describe('sentryOnBuildEnd', () => {
const mockSentryCliInstance = {
releases: {
Expand Down Expand Up @@ -47,7 +52,7 @@ describe('sentryOnBuildEnd', () => {
project: 'test-project',
debug: false,
},
},
} as unknown as TestConfig,
};

beforeEach(() => {
Expand All @@ -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);
Expand All @@ -92,7 +142,7 @@ describe('sentryOnBuildEnd', () => {
enabled: true,
},
},
},
} as unknown as TestConfig,
};

await sentryOnBuildEnd(config);
Expand All @@ -113,7 +163,7 @@ describe('sentryOnBuildEnd', () => {
enabled: false,
},
},
},
} as unknown as TestConfig,
};

await sentryOnBuildEnd(config);
Expand Down Expand Up @@ -141,7 +191,7 @@ describe('sentryOnBuildEnd', () => {
filesToDeleteAfterUpload: '/custom/**/*.map',
},
},
},
} as unknown as TestConfig,
};

await sentryOnBuildEnd(config);
Expand All @@ -166,7 +216,7 @@ describe('sentryOnBuildEnd', () => {
name: 'v1.0.0',
},
},
},
} as unknown as TestConfig,
};

await sentryOnBuildEnd(config);
Expand All @@ -186,7 +236,7 @@ describe('sentryOnBuildEnd', () => {
enabled: true,
},
},
},
} as unknown as TestConfig,
};

await sentryOnBuildEnd(config);
Expand Down Expand Up @@ -225,7 +275,7 @@ describe('sentryOnBuildEnd', () => {
...defaultConfig.viteConfig.sentryConfig,
debug: true,
},
},
} as unknown as TestConfig,
};

await sentryOnBuildEnd(config);
Expand All @@ -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));
});
});
Original file line number Diff line number Diff line change
@@ -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');
});
});
Loading
Loading