diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 77ddae4705b6..70535bd06e89 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1003,7 +1003,7 @@ jobs: working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }} timeout-minutes: 5 run: pnpm test:assert - + - name: Upload Playwright Traces uses: actions/upload-artifact@v4 if: failure() diff --git a/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts b/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts index b3c737efe5ba..0b9a5553fb0a 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts @@ -1,3 +1,8 @@ +import { sentrySolidStartVite } from '@sentry/solidstart'; import { defineConfig } from '@solidjs/start/config'; -export default defineConfig({}); +export default defineConfig({ + vite: { + plugins: [sentrySolidStartVite()], + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/solidstart/package.json b/dev-packages/e2e-tests/test-applications/solidstart/package.json index dfcf8a47402a..e831a14c1f47 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart/package.json @@ -3,14 +3,19 @@ "version": "0.0.0", "scripts": { "clean": "pnpx rimraf node_modules pnpm-lock.yaml .vinxi .output", + "clean:build": "pnpx rimraf .vinxi .output", "dev": "NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev", "build": "vinxi build", "//": [ "We are using `vinxi dev` to start the server because `vinxi start` is experimental and ", "doesn't correctly resolve modules for @sentry/solidstart/solidrouter.", - "This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177" + "This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177", + "We run the build command to ensure building succeeds. However, keeping", + "build output around slows down the vite dev server when using `@sentry/vite-plugin` so we clear it out", + "before actually running the tests.", + "Cleaning the build output should be removed once we can use `vinxi start`." ], - "preview": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev", + "preview": "pnpm clean:build && HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev", "start": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi start", "test:prod": "TEST_ENV=production playwright test", "test:build": "pnpm install && npx playwright install && pnpm build", diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx index e997e4fbb1e3..5e405e8c4e40 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx +++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx @@ -1,75 +1,15 @@ -import * as Sentry from '@sentry/solidstart'; -import type { ParentProps } from 'solid-js'; -import { ErrorBoundary, createSignal, onMount } from 'solid-js'; - -const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary); - -const [count, setCount] = createSignal(1); -const [caughtError, setCaughtError] = createSignal(false); - export default function ClientErrorPage() { return ( - - {caughtError() && ( - - )} -
-
- -
-
- -
-
-
- ); -} - -function Throw(props: { error: string }) { - onMount(() => { - throw new Error(props.error); - }); - return null; -} - -function SampleErrorBoundary(props: ParentProps) { - return ( - ( -
-

Error Boundary Fallback

-
- {error.message} -
- -
- )} - > - {props.children} -
+
+ +
); } diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/error-boundary.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/error-boundary.tsx new file mode 100644 index 000000000000..b22607667e7e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/error-boundary.tsx @@ -0,0 +1,64 @@ +import * as Sentry from '@sentry/solidstart'; +import type { ParentProps } from 'solid-js'; +import { ErrorBoundary, createSignal, onMount } from 'solid-js'; + +const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary); + +const [count, setCount] = createSignal(1); +const [caughtError, setCaughtError] = createSignal(false); + +export default function ErrorBoundaryTestPage() { + return ( + + {caughtError() && ( + + )} +
+
+ +
+
+
+ ); +} + +function Throw(props: { error: string }) { + onMount(() => { + throw new Error(props.error); + }); + return null; +} + +function SampleErrorBoundary(props: ParentProps) { + return ( + ( +
+

Error Boundary Fallback

+
+ {error.message} +
+ +
+ )} + > + {props.children} +
+ ); +} diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx index eed722cba4e3..9a0b22cc38c6 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx +++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx @@ -14,6 +14,9 @@ export default function Home() {
  • Server error
  • +
  • + Error Boundary +
  • User 5 diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts index a4edf3c46236..b709760aab94 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts @@ -10,7 +10,7 @@ test('captures an exception', async ({ page }) => { ); }); - await page.goto('/client-error'); + await page.goto('/error-boundary'); await page.locator('#caughtErrorBtn').click(); const errorEvent = await errorEventPromise; @@ -27,7 +27,7 @@ test('captures an exception', async ({ page }) => { }, ], }, - transaction: '/client-error', + transaction: '/error-boundary', }); }); @@ -40,7 +40,7 @@ test('captures a second exception after resetting the boundary', async ({ page } ); }); - await page.goto('/client-error'); + await page.goto('/error-boundary'); await page.locator('#caughtErrorBtn').click(); const firstErrorEvent = await firstErrorEventPromise; @@ -57,7 +57,7 @@ test('captures a second exception after resetting the boundary', async ({ page } }, ], }, - transaction: '/client-error', + transaction: '/error-boundary', }); const secondErrorEventPromise = waitForError('solidstart', errorEvent => { @@ -85,6 +85,6 @@ test('captures a second exception after resetting the boundary', async ({ page } }, ], }, - transaction: '/client-error', + transaction: '/error-boundary', }); }); diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts index 0f5ef61b365a..e2b00786f54c 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts @@ -4,7 +4,7 @@ import { waitForError } from '@sentry-internal/test-utils'; test.describe('client-side errors', () => { test('captures error thrown on click', async ({ page }) => { const errorPromise = waitForError('solidstart', async errorEvent => { - return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Solid Start E2E test app'; + return errorEvent?.exception?.values?.[0]?.value === 'Uncaught error thrown from Solid Start E2E test app'; }); await page.goto(`/client-error`); @@ -16,9 +16,8 @@ test.describe('client-side errors', () => { values: [ { type: 'Error', - value: 'Error thrown from Solid Start E2E test app', + value: 'Uncaught error thrown from Solid Start E2E test app', mechanism: { - type: 'instrument', handled: false, }, }, diff --git a/packages/solidstart/.eslintrc.js b/packages/solidstart/.eslintrc.js index c1f55c94aadf..d567b12530d0 100644 --- a/packages/solidstart/.eslintrc.js +++ b/packages/solidstart/.eslintrc.js @@ -14,6 +14,7 @@ module.exports = { files: ['src/vite/**', 'src/server/**'], rules: { '@sentry-internal/sdk/no-optional-chaining': 'off', + '@sentry-internal/sdk/no-nullish-coalescing': 'off', }, }, ], diff --git a/packages/solidstart/README.md b/packages/solidstart/README.md index 0b25a3a37e3e..1bb191994c79 100644 --- a/packages/solidstart/README.md +++ b/packages/solidstart/README.md @@ -157,58 +157,34 @@ render( ); ``` -# Sourcemaps and Releases +## Uploading Source Maps -To generate and upload source maps of your Solid Start app use our Vite bundler plugin. - -1. Install the Sentry Vite plugin - -```bash -# Using npm -npm install @sentry/vite-plugin --save-dev - -# Using yarn -yarn add @sentry/vite-plugin --dev -``` - -2. Configure the vite plugin - -To upload source maps you have to configure an auth token. Auth tokens can be passed to the plugin explicitly with the -`authToken` option, with a `SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the -working directory when building your project. We recommend you add the auth token to your CI/CD environment as an -environment variable. +To upload source maps, add the `sentrySolidStartVite` plugin from `@sentry/solidstart` to your `app.config.ts` and +configure an auth token. Auth tokens can be passed to the plugin explicitly with the `authToken` option, with a +`SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the working directory when +building your project. We recommend you add the auth token to your CI/CD environment as an environment variable. Learn more about configuring the plugin in our [Sentry Vite Plugin documentation](https://www.npmjs.com/package/@sentry/vite-plugin). -```bash -// .env.sentry-build-plugin -SENTRY_AUTH_TOKEN= -SENTRY_ORG= -SENTRY_PROJECT= -``` - -3. Finally, add the plugin to your `app.config.ts` file. - -```javascript +```typescript +// app.config.ts import { defineConfig } from '@solidjs/start/config'; -import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { sentrySolidStartVite } from '@sentry/solidstart'; export default defineConfig({ - // rest of your config // ... vite: { - build: { - sourcemap: true, - }, plugins: [ - sentryVitePlugin({ + sentrySolidStartVite({ org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, + debug: true, }), ], }, + // ... }); ``` diff --git a/packages/solidstart/src/index.server.ts b/packages/solidstart/src/index.server.ts index 0ce5251aa327..d675a1c72820 100644 --- a/packages/solidstart/src/index.server.ts +++ b/packages/solidstart/src/index.server.ts @@ -1 +1,2 @@ export * from './server'; +export * from './vite'; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 89eaa14662e3..51adf848775a 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -3,6 +3,7 @@ // exports in this file - which we do below. export * from './client'; export * from './server'; +export * from './vite'; import type { Integration, Options, StackParser } from '@sentry/types'; diff --git a/packages/solidstart/src/vite/index.ts b/packages/solidstart/src/vite/index.ts new file mode 100644 index 000000000000..464bbd604fbe --- /dev/null +++ b/packages/solidstart/src/vite/index.ts @@ -0,0 +1 @@ +export * from './sentrySolidStartVite'; diff --git a/packages/solidstart/src/vite/sentrySolidStartVite.ts b/packages/solidstart/src/vite/sentrySolidStartVite.ts new file mode 100644 index 000000000000..59435f919071 --- /dev/null +++ b/packages/solidstart/src/vite/sentrySolidStartVite.ts @@ -0,0 +1,18 @@ +import type { Plugin } from 'vite'; +import { makeSourceMapsVitePlugin } from './sourceMaps'; +import type { SentrySolidStartPluginOptions } from './types'; + +/** + * Various Sentry vite plugins to be used for SolidStart. + */ +export const sentrySolidStartVite = (options: SentrySolidStartPluginOptions = {}): Plugin[] => { + const sentryPlugins: Plugin[] = []; + + if (process.env.NODE_ENV !== 'development') { + if (options.sourceMapsUploadOptions?.enabled ?? true) { + sentryPlugins.push(...makeSourceMapsVitePlugin(options)); + } + } + + return sentryPlugins; +}; diff --git a/packages/solidstart/src/vite/sourceMaps.ts b/packages/solidstart/src/vite/sourceMaps.ts new file mode 100644 index 000000000000..548038515e79 --- /dev/null +++ b/packages/solidstart/src/vite/sourceMaps.ts @@ -0,0 +1,58 @@ +import { sentryVitePlugin } from '@sentry/vite-plugin'; +import type { Plugin } from 'vite'; +import type { SentrySolidStartPluginOptions } from './types'; + +/** + * A Sentry plugin for SolidStart to enable source maps and use + * @sentry/vite-plugin to automatically upload source maps to Sentry. + * @param {SourceMapsOptions} options + */ +export function makeSourceMapsVitePlugin(options: SentrySolidStartPluginOptions): Plugin[] { + const { authToken, debug, org, project, sourceMapsUploadOptions } = options; + return [ + { + name: 'sentry-solidstart-source-maps', + apply: 'build', + enforce: 'post', + config(config) { + const sourceMapsPreviouslyNotEnabled = !config.build?.sourcemap; + if (debug && sourceMapsPreviouslyNotEnabled) { + // eslint-disable-next-line no-console + console.log('[Sentry SolidStart Plugin] Enabling source map generation'); + if (!sourceMapsUploadOptions?.filesToDeleteAfterUpload) { + // eslint-disable-next-line no-console + console.warn( + `[Sentry SolidStart PLugin] We recommend setting the \`sourceMapsUploadOptions.filesToDeleteAfterUpload\` option to clean up source maps after uploading. +[Sentry SolidStart Plugin] Otherwise, source maps might be deployed to production, depending on your configuration`, + ); + } + } + return { + ...config, + build: { + ...config.build, + sourcemap: true, + }, + }; + }, + }, + ...sentryVitePlugin({ + authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN, + bundleSizeOptimizations: options.bundleSizeOptimizations, + debug: debug ?? false, + org: org ?? process.env.SENTRY_ORG, + project: project ?? process.env.SENTRY_PROJECT, + sourcemaps: { + filesToDeleteAfterUpload: sourceMapsUploadOptions?.filesToDeleteAfterUpload ?? undefined, + ...sourceMapsUploadOptions?.unstable_sentryVitePluginOptions?.sourcemaps, + }, + telemetry: sourceMapsUploadOptions?.telemetry ?? true, + _metaOptions: { + telemetry: { + metaFramework: 'solidstart', + }, + }, + ...sourceMapsUploadOptions?.unstable_sentryVitePluginOptions, + }), + ]; +} diff --git a/packages/solidstart/src/vite/types.ts b/packages/solidstart/src/vite/types.ts new file mode 100644 index 000000000000..4a64e4856b5d --- /dev/null +++ b/packages/solidstart/src/vite/types.ts @@ -0,0 +1,128 @@ +import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; + +type SourceMapsOptions = { + /** + * If this flag is `true`, and an auth token is detected, the Sentry SDK will + * automatically generate and upload source maps to Sentry during a production build. + * + * @default true + */ + enabled?: boolean; + + /** + * If this flag is `true`, the Sentry plugin will collect some telemetry data and send it to Sentry. + * It will not collect any sensitive or user-specific data. + * + * @default true + */ + telemetry?: boolean; + + /** + * A glob or an array of globs that specifies the build artifacts that should be deleted after the artifact + * upload to Sentry has been completed. + * + * @default [] - By default no files are deleted. + * + * The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob) + */ + filesToDeleteAfterUpload?: string | Array; + + /** + * Options to further customize the Sentry Vite Plugin (@sentry/vite-plugin) behavior directly. + * Options specified in this object take precedence over the options specified in + * the `sourcemaps` and `release` objects. + * + * @see https://www.npmjs.com/package/@sentry/vite-plugin/v/2.22.2#options which lists all available options. + * + * Warning: Options within this object are subject to change at any time. + * We DO NOT guarantee semantic versioning for these options, meaning breaking + * changes can occur at any time within a major SDK version. + * + * Furthermore, some options are untested with SvelteKit specifically. Use with caution. + */ + unstable_sentryVitePluginOptions?: Partial; +}; + +type BundleSizeOptimizationOptions = { + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) any debugging code within the Sentry SDK. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * Setting this option to `true` will disable features like the SDK's `debug` option. + */ + excludeDebugStatements?: boolean; + + /** + * If set to true, the plugin will try to tree-shake tracing statements out. + * Note that the success of this depends on tree shaking generally being enabled in your build. + * Attention: DO NOT enable this when you're using any performance monitoring-related SDK features (e.g. Sentry.startSpan()). + */ + excludeTracing?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay Shadow DOM recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * This option is safe to be used when you do not want to capture any Shadow DOM activity via Sentry Session Replay. + */ + excludeReplayShadowDom?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay `iframe` recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * You can safely do this when you do not want to capture any `iframe` activity via Sentry Session Replay. + */ + excludeReplayIframe?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay's Compression Web Worker. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * **Notice:** You should only do use this option if you manually host a compression worker and configure it in your Sentry Session Replay integration config via the `workerUrl` option. + */ + excludeReplayWorker?: boolean; +}; + +/** + * Build options for the Sentry module. These options are used during build-time by the Sentry SDK. + */ +export type SentrySolidStartPluginOptions = { + /** + * The auth token to use when uploading source maps to Sentry. + * + * Instead of specifying this option, you can also set the `SENTRY_AUTH_TOKEN` environment variable. + * + * To create an auth token, follow this guide: + * @see https://docs.sentry.io/product/accounts/auth-tokens/#organization-auth-tokens + */ + authToken?: string; + + /** + * The organization slug of your Sentry organization. + * Instead of specifying this option, you can also set the `SENTRY_ORG` environment variable. + */ + org?: string; + + /** + * The project slug of your Sentry project. + * Instead of specifying this option, you can also set the `SENTRY_PROJECT` environment variable. + */ + project?: string; + + /** + * Options for the Sentry Vite plugin to customize the source maps upload process. + */ + sourceMapsUploadOptions?: SourceMapsOptions; + + /** + * Options for the Sentry Vite plugin to customize bundle size optimizations. + */ + bundleSizeOptimizations?: BundleSizeOptimizationOptions; + + /** + * Enable debug functionality of the SDK during build-time. + * Enabling this will give you, for example logs about source maps. + */ + debug?: boolean; +}; diff --git a/packages/solidstart/test/server/withServerActionInstrumentation.test.ts b/packages/solidstart/test/server/withServerActionInstrumentation.test.ts index 9a5b1e0c2b51..7e1686e2ccb1 100644 --- a/packages/solidstart/test/server/withServerActionInstrumentation.test.ts +++ b/packages/solidstart/test/server/withServerActionInstrumentation.test.ts @@ -10,7 +10,6 @@ import { spanToJSON, } from '@sentry/node'; import { NodeClient } from '@sentry/node'; -import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidrouter'; import { redirect } from '@solidjs/router'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -98,7 +97,6 @@ describe('withServerActionInstrumentation', () => { setCurrentClient(client); client.on('spanStart', span => spanStartMock(spanToJSON(span))); - client.addIntegration(solidRouterBrowserTracingIntegration()); await serverActionGetPrefecture(); expect(spanStartMock).toHaveBeenCalledWith( diff --git a/packages/solidstart/test/vite/sentrySolidStartVite.test.ts b/packages/solidstart/test/vite/sentrySolidStartVite.test.ts new file mode 100644 index 000000000000..d3f905313859 --- /dev/null +++ b/packages/solidstart/test/vite/sentrySolidStartVite.test.ts @@ -0,0 +1,50 @@ +import type { Plugin } from 'vite'; +import { describe, expect, it, vi } from 'vitest'; +import { sentrySolidStartVite } from '../../src/vite/sentrySolidStartVite'; + +vi.spyOn(console, 'log').mockImplementation(() => { + /* noop */ +}); +vi.spyOn(console, 'warn').mockImplementation(() => { + /* noop */ +}); + +function getSentrySolidStartVitePlugins(options?: Parameters[0]): Plugin[] { + return sentrySolidStartVite({ + project: 'project', + org: 'org', + authToken: 'token', + ...options, + }); +} + +describe('sentrySolidStartVite()', () => { + it('returns an array of vite plugins', () => { + const plugins = getSentrySolidStartVitePlugins(); + const names = plugins.map(plugin => plugin.name); + expect(names).toEqual([ + 'sentry-solidstart-source-maps', + 'sentry-telemetry-plugin', + 'sentry-vite-release-injection-plugin', + 'sentry-debug-id-upload-plugin', + 'sentry-vite-debug-id-injection-plugin', + 'sentry-vite-debug-id-upload-plugin', + 'sentry-file-deletion-plugin', + ]); + }); + + it("returns an empty array if source maps upload isn't enabled", () => { + const plugins = getSentrySolidStartVitePlugins({ sourceMapsUploadOptions: { enabled: false } }); + expect(plugins).toHaveLength(0); + }); + + it('returns an empty array if `NODE_ENV` is development', async () => { + const previousEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + const plugins = getSentrySolidStartVitePlugins({ sourceMapsUploadOptions: { enabled: true } }); + expect(plugins).toHaveLength(0); + + process.env.NODE_ENV = previousEnv; + }); +}); diff --git a/packages/solidstart/test/vite/sourceMaps.test.ts b/packages/solidstart/test/vite/sourceMaps.test.ts new file mode 100644 index 000000000000..e7d6c1bd598d --- /dev/null +++ b/packages/solidstart/test/vite/sourceMaps.test.ts @@ -0,0 +1,96 @@ +import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { makeSourceMapsVitePlugin } from '../../src/vite/sourceMaps'; + +const mockedSentryVitePlugin = { + name: 'sentry-vite-debug-id-upload-plugin', + writeBundle: vi.fn(), +}; + +const sentryVitePluginSpy = vi.fn((_options: SentryVitePluginOptions) => [mockedSentryVitePlugin]); + +vi.mock('@sentry/vite-plugin', async () => { + const original = (await vi.importActual('@sentry/vite-plugin')) as any; + + return { + ...original, + sentryVitePlugin: (options: SentryVitePluginOptions) => sentryVitePluginSpy(options), + }; +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe('makeSourceMapsVitePlugin()', () => { + it('returns a plugin to set `sourcemaps` to `true`', () => { + const [sourceMapsConfigPlugin, sentryVitePlugin] = makeSourceMapsVitePlugin({}); + + expect(sourceMapsConfigPlugin?.name).toEqual('sentry-solidstart-source-maps'); + expect(sourceMapsConfigPlugin?.apply).toEqual('build'); + expect(sourceMapsConfigPlugin?.enforce).toEqual('post'); + expect(sourceMapsConfigPlugin?.config).toEqual(expect.any(Function)); + + expect(sentryVitePlugin).toEqual(mockedSentryVitePlugin); + }); + + it('passes user-specified vite plugin options to vite plugin plugin', () => { + makeSourceMapsVitePlugin({ + org: 'my-org', + authToken: 'my-token', + sourceMapsUploadOptions: { + filesToDeleteAfterUpload: ['baz/*.js'], + }, + bundleSizeOptimizations: { + excludeTracing: true, + }, + }); + + expect(sentryVitePluginSpy).toHaveBeenCalledWith( + expect.objectContaining({ + org: 'my-org', + authToken: 'my-token', + sourcemaps: { + filesToDeleteAfterUpload: ['baz/*.js'], + }, + bundleSizeOptimizations: { + excludeTracing: true, + }, + }), + ); + }); + + it('should override options with unstable_sentryVitePluginOptions', () => { + makeSourceMapsVitePlugin({ + org: 'my-org', + authToken: 'my-token', + bundleSizeOptimizations: { + excludeTracing: true, + }, + sourceMapsUploadOptions: { + unstable_sentryVitePluginOptions: { + org: 'unstable-org', + sourcemaps: { + assets: ['unstable/*.js'], + }, + bundleSizeOptimizations: { + excludeTracing: false, + }, + }, + }, + }); + + expect(sentryVitePluginSpy).toHaveBeenCalledWith( + expect.objectContaining({ + org: 'unstable-org', + authToken: 'my-token', + bundleSizeOptimizations: { + excludeTracing: false, + }, + sourcemaps: { + assets: ['unstable/*.js'], + }, + }), + ); + }); +});