diff --git a/packages/nuxt/src/common/types.ts b/packages/nuxt/src/common/types.ts index afcf0a327d52..2f374c0b92a4 100644 --- a/packages/nuxt/src/common/types.ts +++ b/packages/nuxt/src/common/types.ts @@ -22,156 +22,6 @@ export type SentryNuxtServerOptions = Parameters[0] & { enableNitroErrorHandler?: boolean; }; -type SourceMapsOptions = { - /** - * Suppresses all logs. - * - * @default false - * @deprecated Use option `silent` instead of `sourceMapsUploadOptions.silent` - */ - silent?: boolean; - - /** - * When an error occurs during release creation or sourcemaps upload, the plugin will call this function. - * - * By default, the plugin will simply throw an error, thereby stopping the bundling process. - * If an `errorHandler` callback is provided, compilation will continue, unless an error is - * thrown in the provided callback. - * - * To allow compilation to continue but still emit a warning, set this option to the following: - * - * ```js - * (err) => { - * console.warn(err); - * } - * ``` - * - * @deprecated Use option `errorHandler` instead of `sourceMapsUploadOptions.errorHandler` - */ - errorHandler?: (err: Error) => void; - - /** - * Options related to managing the Sentry releases for a build. - * - * More info: https://docs.sentry.io/product/releases/ - * - * @deprecated Use option `release` instead of `sourceMapsUploadOptions.release` - */ - release?: { - /** - * Unique identifier for the release you want to create. - * - * This value can also be specified via the `SENTRY_RELEASE` environment variable. - * - * Defaults to automatically detecting a value for your environment. - * This includes values for Cordova, Heroku, AWS CodeBuild, CircleCI, Xcode, and Gradle, and otherwise uses the git `HEAD`'s commit SHA. - * (the latter requires access to git CLI and for the root directory to be a valid repository) - * - * If you didn't provide a value and the plugin can't automatically detect one, no release will be created. - * - * @deprecated Use `release.name` instead of `sourceMapsUploadOptions.release.name` - */ - name?: string; - }; - - /** - * 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 - * @deprecated Use option `sourcemaps.disable` instead of `sourceMapsUploadOptions.enabled` - */ - enabled?: boolean; - - /** - * 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 - * @deprecated Use option `authToken` instead of `sourceMapsUploadOptions.authToken` - */ - authToken?: string; - - /** - * The organization slug of your Sentry organization. - * Instead of specifying this option, you can also set the `SENTRY_ORG` environment variable. - * @deprecated Use option `org` instead of `sourceMapsUploadOptions.org` - */ - org?: string; - - /** - * The URL of your Sentry instance if you're using self-hosted Sentry. - * - * @default https://sentry.io by default the plugin will point towards the Sentry SaaS URL - * @deprecated Use `sentryUrl` instead of `sourceMapsUploadOptions.url` - */ - url?: string; - - /** - * The project slug of your Sentry project. - * Instead of specifying this option, you can also set the `SENTRY_PROJECT` environment variable. - * - * @deprecated Use option `project` instead of `sourceMapsUploadOptions.project` - */ - project?: string; - - /** - * 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 - * @deprecated Use option `telemetry` instead of `sourceMapsUploadOptions.telemetry` - */ - telemetry?: boolean; - - /** - * Options related to sourcemaps - * - * @deprecated Use option `sourcemaps` instead of `sourceMapsUploadOptions.sourcemaps` - */ - sourcemaps?: { - /** - * A glob or an array of globs that specify the build artifacts and source maps that will be uploaded to Sentry. - * - * If this option is not specified, sensible defaults based on your adapter and nuxt.config.js - * setup will be used. Use this option to override these defaults, for instance if you have a - * customized build setup that diverges from Nuxt's defaults. - * - * The globbing patterns must follow the implementation of the `glob` package. - * @see https://www.npmjs.com/package/glob#glob-primer - * - * @deprecated Use option `sourcemaps.assets` instead of `sourceMapsUploadOptions.sourcemaps.assets` - */ - assets?: string | Array; - - /** - * A glob or an array of globs that specifies which build artifacts should not be uploaded to Sentry. - * - * @default [] - By default no files are ignored. Thus, all files matching the `assets` glob - * or the default value for `assets` are uploaded. - * - * The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob) - * - * @deprecated Use option `sourcemaps.ignore` instead of `sourceMapsUploadOptions.sourcemaps.ignore` - */ - ignore?: string | Array; - - /** - * 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) - * - * @deprecated Use option `sourcemaps.filesToDeleteAfterUpload` instead of `sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload` - */ - filesToDeleteAfterUpload?: string | Array; - }; -}; - /** * Build options for the Sentry module. These options are used during build-time by the Sentry SDK. */ @@ -200,17 +50,6 @@ export type SentryNuxtModuleOptions = BuildTimeOptionsBase & { useDiagnosticsChannelInjection?: boolean; }; - /** - * Options for the Sentry Vite plugin to customize the source maps upload process. - * - * These options are always read from the `sentry` module options in the `nuxt.config.(js|ts). - * Do not define them in the `sentry.client.config.(js|ts)` or `sentry.server.config.(js|ts)` files. - * - * @deprecated This option was deprecated as it adds unnecessary nesting. - * Put the options one level higher to the root-level of the `sentry` module options. - */ - sourceMapsUploadOptions?: SourceMapsOptions; - /** * * Enables (partial) server tracing by automatically injecting Sentry for environments where modifying the node option `--import` is not possible. diff --git a/packages/nuxt/src/vite/sourceMaps.ts b/packages/nuxt/src/vite/sourceMaps.ts index faf68b2bc37d..235c3d4e2576 100644 --- a/packages/nuxt/src/vite/sourceMaps.ts +++ b/packages/nuxt/src/vite/sourceMaps.ts @@ -22,20 +22,9 @@ export function setupSourceMaps( nuxt: Nuxt, addVitePlugin: (plugin: Plugin[], options?: { dev?: boolean; build?: boolean }) => void, ): void { - // TODO(v11): remove deprecated options (also from SentryNuxtModuleOptions type) - const isDebug = moduleOptions.debug; - // eslint-disable-next-line typescript/no-deprecated - const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; - - const sourceMapsEnabled = - moduleOptions.sourcemaps?.disable === true - ? false - : moduleOptions.sourcemaps?.disable === false - ? true - : // eslint-disable-next-line typescript/no-deprecated - (sourceMapsUploadOptions.enabled ?? true); + const sourceMapsEnabled = moduleOptions.sourcemaps?.disable !== true; // In case we overwrite the source map settings, we default to deleting the files const shouldDeleteFilesFallback = { client: true, server: true }; @@ -62,11 +51,7 @@ export function setupSourceMaps( ? 'server-side' : 'client-side'; - if ( - !moduleOptions.sourcemaps?.filesToDeleteAfterUpload && - // eslint-disable-next-line typescript/no-deprecated - !sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload - ) { + if (!moduleOptions.sourcemaps?.filesToDeleteAfterUpload) { // eslint-disable-next-line no-console console.log( `[Sentry] We enabled \`'hidden'\` source maps for your ${enabledDeleteFallbacks} build. Source map files will be automatically deleted after uploading them to Sentry.`, @@ -131,65 +116,27 @@ function normalizePath(path: string): string { * * Only exported for Testing purposes. */ -// todo(v11): This "eslint-disable" can be removed again once we remove deprecated options. -// eslint-disable-next-line complexity export function getPluginOptions( moduleOptions: SentryNuxtModuleOptions, shouldDeleteFilesFallback?: { client: boolean; server: boolean }, ): SentryVitePluginOptions | SentryRollupPluginOptions { - // eslint-disable-next-line typescript/no-deprecated - const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; - - const shouldDeleteFilesAfterUpload = shouldDeleteFilesFallback?.client || shouldDeleteFilesFallback?.server; - const fallbackFilesToDelete = [ - ...(shouldDeleteFilesFallback?.client ? ['.*/**/public/**/*.map'] : []), - ...(shouldDeleteFilesFallback?.server - ? ['.*/**/server/**/*.map', '.*/**/output/**/*.map', '.*/**/function/**/*.map'] - : []), - ]; - - // Check for filesToDeleteAfterUpload in new location first, then deprecated location const sourcemapsOptions = moduleOptions.sourcemaps || {}; - // eslint-disable-next-line typescript/no-deprecated - const deprecatedSourcemapsOptions = sourceMapsUploadOptions.sourcemaps || {}; - - const filesToDeleteAfterUpload = - sourcemapsOptions.filesToDeleteAfterUpload ?? - // eslint-disable-next-line typescript/no-deprecated - deprecatedSourcemapsOptions.filesToDeleteAfterUpload; - - if (typeof filesToDeleteAfterUpload === 'undefined' && shouldDeleteFilesAfterUpload && moduleOptions.debug) { - // eslint-disable-next-line no-console - console.log( - `[Sentry] Setting \`sentry.sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload: [${fallbackFilesToDelete - // Logging it as strings in the array - .map(path => `"${path}"`) - .join(', ')}]\` to delete generated source maps after they were uploaded to Sentry.`, - ); - } + const filesToDeleteAfterUpload = resolveFilesToDeleteAfterUpload(moduleOptions, shouldDeleteFilesFallback); return { applicationKey: moduleOptions.applicationKey, - // eslint-disable-next-line typescript/no-deprecated - org: moduleOptions.org ?? sourceMapsUploadOptions.org ?? process.env.SENTRY_ORG, - // eslint-disable-next-line typescript/no-deprecated - project: moduleOptions.project ?? sourceMapsUploadOptions.project ?? process.env.SENTRY_PROJECT, - // eslint-disable-next-line typescript/no-deprecated - authToken: moduleOptions.authToken ?? sourceMapsUploadOptions.authToken ?? process.env.SENTRY_AUTH_TOKEN, - // eslint-disable-next-line typescript/no-deprecated - telemetry: moduleOptions.telemetry ?? sourceMapsUploadOptions.telemetry ?? true, - // eslint-disable-next-line typescript/no-deprecated - url: moduleOptions.sentryUrl ?? sourceMapsUploadOptions.url ?? process.env.SENTRY_URL, + org: moduleOptions.org ?? process.env.SENTRY_ORG, + project: moduleOptions.project ?? process.env.SENTRY_PROJECT, + authToken: moduleOptions.authToken ?? process.env.SENTRY_AUTH_TOKEN, + telemetry: moduleOptions.telemetry ?? true, + url: moduleOptions.sentryUrl ?? process.env.SENTRY_URL, headers: moduleOptions.headers, debug: moduleOptions.debug ?? false, - // eslint-disable-next-line typescript/no-deprecated - silent: moduleOptions.silent ?? sourceMapsUploadOptions.silent ?? false, - // eslint-disable-next-line typescript/no-deprecated - errorHandler: moduleOptions.errorHandler ?? sourceMapsUploadOptions.errorHandler, + silent: moduleOptions.silent ?? false, + errorHandler: moduleOptions.errorHandler, bundleSizeOptimizations: moduleOptions.bundleSizeOptimizations, // todo: test if this can be overridden by the user release: { - // eslint-disable-next-line typescript/no-deprecated - name: moduleOptions.release?.name ?? sourceMapsUploadOptions.release?.name, + name: moduleOptions.release?.name, // Support all release options from BuildTimeOptionsBase ...moduleOptions.release, ...moduleOptions?.unstable_sentryBundlerPluginOptions?.release, @@ -206,21 +153,51 @@ export function getPluginOptions( // The server/client files are in different places depending on the nitro preset (e.g. '.output/server' or '.netlify/functions-internal/server') // We cannot determine automatically how the build folder looks like (depends on the preset), so we have to accept that source maps are uploaded multiple times (with the vitePlugin for Nuxt and the rollupPlugin for Nitro). // If we could know where the server/client assets are located, we could do something like this (based on the Nitro preset): isNitro ? ['./.output/server/**/*'] : ['./.output/public/**/*'], - // eslint-disable-next-line typescript/no-deprecated - assets: sourcemapsOptions.assets ?? deprecatedSourcemapsOptions.assets ?? undefined, - // eslint-disable-next-line typescript/no-deprecated - ignore: sourcemapsOptions.ignore ?? deprecatedSourcemapsOptions.ignore ?? undefined, - filesToDeleteAfterUpload: filesToDeleteAfterUpload - ? filesToDeleteAfterUpload - : shouldDeleteFilesFallback?.server || shouldDeleteFilesFallback?.client - ? fallbackFilesToDelete - : undefined, + assets: sourcemapsOptions.assets ?? undefined, + ignore: sourcemapsOptions.ignore ?? undefined, + filesToDeleteAfterUpload, rewriteSources: sourcemapsOptions.rewriteSources ?? normalizePath, ...moduleOptions?.unstable_sentryBundlerPluginOptions?.sourcemaps, }, }; } +/** + * Users can set `filesToDeleteAfterUpload` themselves. If they don't, we fall back to deleting the + * client/server source maps — but only the ones Sentry generated itself (i.e. when the user didn't + * configure source maps at all). If the user explicitly set source maps, we leave their files alone. + */ +function resolveFilesToDeleteAfterUpload( + moduleOptions: SentryNuxtModuleOptions, + shouldDeleteFilesFallback?: { client: boolean; server: boolean }, +): string | Array | undefined { + const shouldDeleteFilesAfterUpload = shouldDeleteFilesFallback?.client || shouldDeleteFilesFallback?.server; + const fallbackFilesToDelete = [ + ...(shouldDeleteFilesFallback?.client ? ['.*/**/public/**/*.map'] : []), + ...(shouldDeleteFilesFallback?.server + ? ['.*/**/server/**/*.map', '.*/**/output/**/*.map', '.*/**/function/**/*.map'] + : []), + ]; + + const filesToDeleteAfterUpload = moduleOptions.sourcemaps?.filesToDeleteAfterUpload; + + if (typeof filesToDeleteAfterUpload === 'undefined' && shouldDeleteFilesAfterUpload && moduleOptions.debug) { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Setting \`sentry.sourcemaps.filesToDeleteAfterUpload: [${fallbackFilesToDelete + // Logging it as strings in the array + .map(path => `"${path}"`) + .join(', ')}]\` to delete generated source maps after they were uploaded to Sentry.`, + ); + } + + return filesToDeleteAfterUpload + ? filesToDeleteAfterUpload + : shouldDeleteFilesAfterUpload + ? fallbackFilesToDelete + : undefined; +} + /* There are multiple ways to set up source maps (https://github.com/getsentry/sentry-javascript/issues/13993 and https://github.com/getsentry/sentry-javascript/pull/15859) 1. User explicitly disabled source maps - keep this setting (emit a warning that errors won't be unminified in Sentry) diff --git a/packages/nuxt/test/vite/buildOptions.test-d.ts b/packages/nuxt/test/vite/buildOptions.test-d.ts index 0eca417d46f0..5b1fa01a4a62 100644 --- a/packages/nuxt/test/vite/buildOptions.test-d.ts +++ b/packages/nuxt/test/vite/buildOptions.test-d.ts @@ -75,49 +75,6 @@ describe('Sentry Nuxt build-time options type', () => { expectTypeOf(completeOptions).toEqualTypeOf(); }); - it('includes all deprecated options', () => { - const completeOptions: SentryNuxtModuleOptions = { - // SentryNuxtModuleOptions specific options - enabled: true, - debug: true, - autoInjectServerSentry: 'experimental_dynamic-import', // No need for 'as const' with type assertion - experimental_entrypointWrappedFunctions: ['default', 'handler', 'server', 'customExport'], - unstable_sentryBundlerPluginOptions: { - // Rollup plugin options - bundleSizeOptimizations: { - excludeDebugStatements: true, - }, - // Vite plugin options - sourcemaps: { - assets: './dist/**/*', - }, - }, - - // Deprecated sourceMapsUploadOptions - sourceMapsUploadOptions: { - silent: false, - // eslint-disable-next-line no-console - errorHandler: (err: Error) => console.warn(err), - release: { - name: 'deprecated-release', - }, - enabled: true, - authToken: 'deprecated-token', - org: 'deprecated-org', - url: 'https://deprecated.sentry.io', - project: 'deprecated-project', - telemetry: false, - sourcemaps: { - assets: './build/**/*', - ignore: ['./build/*.spec.js'], - filesToDeleteAfterUpload: ['./build/*.map'], - }, - }, - }; - - expectTypeOf(completeOptions).toEqualTypeOf(); - }); - it('allows partial configuration', () => { const minimalOptions: SentryNuxtModuleOptions = { enabled: true }; diff --git a/packages/nuxt/test/vite/sourceMaps-nuxtHooks.test.ts b/packages/nuxt/test/vite/sourceMaps-nuxtHooks.test.ts index a9edb03fb1ed..630789364f20 100644 --- a/packages/nuxt/test/vite/sourceMaps-nuxtHooks.test.ts +++ b/packages/nuxt/test/vite/sourceMaps-nuxtHooks.test.ts @@ -109,6 +109,17 @@ describe('setupSourceMaps hooks', () => { expect(mockAddVitePlugin).not.toHaveBeenCalled(); }); + it('does not add plugins when source maps are disabled via `sourcemaps.disable`', async () => { + const { setupSourceMaps } = await import('../../src/vite/sourceMaps'); + const mockNuxt = createMockNuxt({}); + const { mockAddVitePlugin } = createMockAddVitePlugin(); + + setupSourceMaps({ sourcemaps: { disable: true } }, mockNuxt as unknown as Nuxt, mockAddVitePlugin); + await mockNuxt.triggerHook('modules:done'); + + expect(mockAddVitePlugin).not.toHaveBeenCalled(); + }); + it.each([ { label: 'server (SSR) build', buildConfig: { build: { ssr: true }, plugins: [] } }, { label: 'client build', buildConfig: { build: { ssr: false }, plugins: [] } }, diff --git a/packages/nuxt/test/vite/sourceMaps.test.ts b/packages/nuxt/test/vite/sourceMaps.test.ts index 28ed1338e510..b5f21b8f7bd3 100644 --- a/packages/nuxt/test/vite/sourceMaps.test.ts +++ b/packages/nuxt/test/vite/sourceMaps.test.ts @@ -79,16 +79,14 @@ describe('getPluginOptions', () => { it('merges custom moduleOptions with default options', () => { const customOptions: SentryNuxtModuleOptions = { - sourceMapsUploadOptions: { - org: 'custom-org', - project: 'custom-project', - authToken: 'custom-token', - telemetry: false, - sourcemaps: { - assets: ['custom-assets/**/*'], - ignore: ['ignore-this.js'], - filesToDeleteAfterUpload: ['delete-this.js'], - }, + org: 'custom-org', + project: 'custom-project', + authToken: 'custom-token', + telemetry: false, + sourcemaps: { + assets: ['custom-assets/**/*'], + ignore: ['ignore-this.js'], + filesToDeleteAfterUpload: ['delete-this.js'], }, debug: true, }; @@ -115,6 +113,35 @@ describe('getPluginOptions', () => { ); }); + it('resolves root-level options into plugin options', () => { + const errorHandler = (err: Error): void => { + throw err; + }; + const result = getPluginOptions({ + org: 'my-org', + project: 'my-project', + authToken: 'my-token', + sentryUrl: 'https://my.sentry.io', + telemetry: false, + silent: true, + errorHandler, + headers: { 'X-Foo': 'bar' }, + release: { name: '1.2.3' }, + } as SentryNuxtModuleOptions); + + expect(result).toMatchObject({ + org: 'my-org', + project: 'my-project', + authToken: 'my-token', + url: 'https://my.sentry.io', // sentryUrl is resolved to the plugin's `url` option + telemetry: false, + silent: true, + errorHandler, + headers: { 'X-Foo': 'bar' }, + release: { name: '1.2.3' }, + }); + }); + it('normalizes source paths via rewriteSources', () => { const options = getPluginOptions({} as SentryNuxtModuleOptions, undefined); const rewrite = options.sourcemaps?.rewriteSources as ((s: string) => string) | undefined; @@ -133,109 +160,6 @@ describe('getPluginOptions', () => { expect(options.sourcemaps?.rewriteSources).toBe(customRewrite); }); - it('prioritizes new BuildTimeOptionsBase options over deprecated ones', () => { - const options: SentryNuxtModuleOptions = { - // New options - org: 'new-org', - project: 'new-project', - authToken: 'new-token', - sentryUrl: 'https://new.sentry.io', - telemetry: false, - silent: true, - debug: true, - sourcemaps: { - assets: ['new-assets/**/*'], - ignore: ['new-ignore.js'], - filesToDeleteAfterUpload: ['new-delete.js'], - }, - release: { - name: 'test-release', - create: false, - finalize: true, - dist: 'build-123', - vcsRemote: 'upstream', - setCommits: { auto: true }, - deploy: { env: 'production' }, - }, - bundleSizeOptimizations: { excludeTracing: true }, - - // Deprecated options (should be ignored) - sourceMapsUploadOptions: { - org: 'old-org', - project: 'old-project', - authToken: 'old-token', - url: 'https://old.sentry.io', - telemetry: true, - silent: false, - sourcemaps: { - assets: ['old-assets/**/*'], - ignore: ['old-ignore.js'], - filesToDeleteAfterUpload: ['old-delete.js'], - }, - release: { name: 'old-release' }, - }, - }; - - const result = getPluginOptions(options); - - expect(result).toMatchObject({ - org: 'new-org', - project: 'new-project', - authToken: 'new-token', - url: 'https://new.sentry.io', - telemetry: false, - silent: true, - debug: true, - bundleSizeOptimizations: { excludeTracing: true }, - release: { - name: 'test-release', - create: false, - finalize: true, - dist: 'build-123', - vcsRemote: 'upstream', - setCommits: { auto: true }, - deploy: { env: 'production' }, - }, - sourcemaps: expect.objectContaining({ - assets: ['new-assets/**/*'], - ignore: ['new-ignore.js'], - filesToDeleteAfterUpload: ['new-delete.js'], - }), - }); - }); - - it('falls back to deprecated options when new ones are undefined', () => { - const options: SentryNuxtModuleOptions = { - debug: true, - sourceMapsUploadOptions: { - org: 'deprecated-org', - project: 'deprecated-project', - authToken: 'deprecated-token', - url: 'https://deprecated.sentry.io', - telemetry: false, - sourcemaps: { - assets: ['deprecated/**/*'], - }, - release: { name: 'deprecated-release' }, - }, - }; - - const result = getPluginOptions(options); - - expect(result).toMatchObject({ - org: 'deprecated-org', - project: 'deprecated-project', - authToken: 'deprecated-token', - url: 'https://deprecated.sentry.io', - telemetry: false, - debug: true, - release: { name: 'deprecated-release' }, - sourcemaps: expect.objectContaining({ - assets: ['deprecated/**/*'], - }), - }); - }); - it('passes applicationKey to plugin options', () => { const options: SentryNuxtModuleOptions = { applicationKey: 'my-app-key', @@ -320,18 +244,7 @@ describe('getPluginOptions', () => { expected: ['.*/**/server/**/*.map', '.*/**/output/**/*.map', '.*/**/function/**/*.map'], }, { - name: 'no fallback, but custom filesToDeleteAfterUpload is provided (deprecated)', - clientFallback: false, - serverFallback: false, - customOptions: { - sourceMapsUploadOptions: { - sourcemaps: { filesToDeleteAfterUpload: ['deprecated/path/**/*.map'] }, - }, - }, - expected: ['deprecated/path/**/*.map'], - }, - { - name: 'no fallback, but custom filesToDeleteAfterUpload is provided (new)', + name: 'no fallback, but custom filesToDeleteAfterUpload is provided', clientFallback: false, serverFallback: false, customOptions: {