From c7b5f462f759a20d8a2cfa19a517e5755af1a736 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 27 Nov 2025 12:41:26 +0200 Subject: [PATCH 1/6] refactor: isolate webpack options in their own namespace and mark toplevel ones for deprecation --- packages/nextjs/src/config/types.ts | 120 ++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 28e038b6d0f2..db587cba0983 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -57,6 +57,106 @@ export type NextConfigObject = { }; }; +export type SentryBuildWebpackOptions = { + /** + * Include Next.js-internal code and code from dependencies when uploading source maps. + * + * Note: Enabling this option can lead to longer build times. + * Disabling this option will leave you without readable stacktraces for dependencies and Next.js-internal code. + * + * Defaults to `false`. + */ + // Enabling this option may upload a lot of source maps and since the sourcemap upload endpoint in Sentry is super + // slow we don't enable it by default so that we don't opaquely increase build times for users. + // TODO: Add an alias to this function called "uploadSourceMapsForDependencies" + widenClientFileUpload?: boolean; + + /** + * Automatically instrument Next.js data fetching methods and Next.js API routes with error and performance monitoring. + * Defaults to `true`. + */ + autoInstrumentServerFunctions?: boolean; + + /** + * Automatically instrument Next.js middleware with error and performance monitoring. Defaults to `true`. + */ + autoInstrumentMiddleware?: boolean; + + /** + * Automatically instrument components in the `app` directory with error monitoring. Defaults to `true`. + */ + autoInstrumentAppDirectory?: boolean; + + /** + * Automatically create cron monitors in Sentry for your Vercel Cron Jobs if configured via `vercel.json`. + * + * Defaults to `false`. + */ + automaticVercelMonitors?: boolean; + + /** + * Exclude certain serverside API routes or pages from being instrumented with Sentry during build-time. This option + * takes an array of strings or regular expressions. This options also affects pages in the `app` directory. + * + * NOTE: Pages should be specified as routes (`/animals` or `/api/animals/[animalType]/habitat`), not filepaths + * (`pages/animals/index.js` or `.\src\pages\api\animals\[animalType]\habitat.tsx`), and strings must be be a full, + * exact match. + * + * Notice: If you build Next.js with turbopack, the Sentry SDK will no longer apply build-time instrumentation and + * purely rely on Next.js telemetry features, meaning that this option will effectively no-op. + */ + excludeServerRoutes?: Array; + + /** + * Disables automatic injection of Sentry's Webpack configuration. + * + * By default, the Sentry Next.js SDK injects its own Webpack configuration to enable features such as + * source map upload and automatic instrumentation. Set this option to `true` if you want to prevent + * the SDK from modifying your Webpack config (for example, if you want to handle Sentry integration manually + * or if you are on an older version of Next.js while using Turbopack). + */ + disableSentryConfig?: boolean; + + /** + * Tree shakes Sentry SDK logger statements from the bundle. + */ + treeshake?: { + /** + * Tree shakes Sentry SDK logger statements from the bundle. Note that this doesn't affect Sentry Logs. + */ + debugLogs?: boolean; + }; + + /** + * Options to be passed directly to the Sentry Webpack Plugin (`@sentry/webpack-plugin`) that ships with the Sentry SDK. + * You can use this option to override any options the SDK passes to the Webpack plugin. + * + * Please note that this option is unstable and may change in a breaking way in any release. + */ + unstable_sentryWebpackPluginOptions?: SentryWebpackPluginOptions; + + /** + * Options related to react component name annotations. + * Disabled by default, unless a value is set for this option. + * When enabled, your app's DOM will automatically be annotated during build-time with their respective component names. + * This will unlock the capability to search for Replays in Sentry by component name, as well as see component names in breadcrumbs and performance monitoring. + * Please note that this feature is not currently supported by the esbuild bundler plugins, and will only annotate React components + * + * @deprecated Use `webpack.reactComponentAnnotation` instead. + */ + reactComponentAnnotation?: { + /** + * Whether the component name annotate plugin should be enabled or not. + */ + enabled?: boolean; + + /** + * A list of strings representing the names of components to ignore. The plugin will not apply `data-sentry` annotations on the DOM element for these components. + */ + ignoredComponents?: string[]; + }; +}; + export type SentryBuildOptions = { /** * The slug of the Sentry organization associated with the app. @@ -363,6 +463,8 @@ export type SentryBuildOptions = { * When enabled, your app's DOM will automatically be annotated during build-time with their respective component names. * This will unlock the capability to search for Replays in Sentry by component name, as well as see component names in breadcrumbs and performance monitoring. * Please note that this feature is not currently supported by the esbuild bundler plugins, and will only annotate React components + * + * @deprecated Use `webpack.reactComponentAnnotation` instead. */ reactComponentAnnotation?: { /** @@ -381,6 +483,7 @@ export type SentryBuildOptions = { * You can use this option to override any options the SDK passes to the webpack plugin. * * Please note that this option is unstable and may change in a breaking way in any release. + * @deprecated Use `webpack.unstable_sentryWebpackPluginOptions` instead. */ unstable_sentryWebpackPluginOptions?: SentryWebpackPluginOptions; @@ -391,6 +494,7 @@ export type SentryBuildOptions = { * Disabling this option will leave you without readable stacktraces for dependencies and Next.js-internal code. * * Defaults to `false`. + * @deprecated Use `webpack.widenClientFileUpload` instead. */ // Enabling this option may upload a lot of source maps and since the sourcemap upload endpoint in Sentry is super // slow we don't enable it by default so that we don't opaquely increase build times for users. @@ -400,16 +504,19 @@ export type SentryBuildOptions = { /** * Automatically instrument Next.js data fetching methods and Next.js API routes with error and performance monitoring. * Defaults to `true`. + * @deprecated Use `webpack.autoInstrumentServerFunctions` instead. */ autoInstrumentServerFunctions?: boolean; /** * Automatically instrument Next.js middleware with error and performance monitoring. Defaults to `true`. + * @deprecated Use `webpack.autoInstrumentMiddleware` instead. */ autoInstrumentMiddleware?: boolean; /** * Automatically instrument components in the `app` directory with error monitoring. Defaults to `true`. + * @deprecated Use `webpack.autoInstrumentAppDirectory` instead. */ autoInstrumentAppDirectory?: boolean; @@ -423,6 +530,8 @@ export type SentryBuildOptions = { * * Notice: If you build Next.js with turbopack, the Sentry SDK will no longer apply build-time instrumentation and * purely rely on Next.js telemetry features, meaning that this option will effectively no-op. + * + * @deprecated Use `webpack.excludeServerRoutes` instead. */ excludeServerRoutes?: Array; @@ -439,6 +548,8 @@ export type SentryBuildOptions = { /** * Tree shakes Sentry SDK logger statements from the bundle. + * + * @deprecated Use `webpack.treeshake.debugLogs` instead. */ disableLogger?: boolean; @@ -446,6 +557,8 @@ export type SentryBuildOptions = { * Automatically create cron monitors in Sentry for your Vercel Cron Jobs if configured via `vercel.json`. * * Defaults to `false`. + * + * @deprecated Use `webpack.automaticVercelMonitors` instead. */ automaticVercelMonitors?: boolean; @@ -497,6 +610,8 @@ export type SentryBuildOptions = { * the SDK from modifying your Webpack config (for example, if you want to handle Sentry integration manually * or if you are on an older version of Next.js while using Turbopack). * + * @deprecated Use `webpack.disableSentryConfig` instead. + * * @default false */ disableSentryWebpackConfig?: boolean; @@ -519,6 +634,11 @@ export type SentryBuildOptions = { _experimental?: Partial<{ thirdPartyOriginStackFrames?: boolean; }>; + + /** + * Options related to webpack builds, has no effect if you are using Turbopack. + */ + webpack?: SentryBuildWebpackOptions; }; export type NextConfigFunction = ( From 43c842a7f7c1d97ce862b18ee391477e3a0866ee Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 27 Nov 2025 12:43:47 +0200 Subject: [PATCH 2/6] refactor: use the new options path but fallback to toplevel options --- .../src/config/getBuildPluginOptions.ts | 12 +-- packages/nextjs/src/config/webpack.ts | 12 +-- .../nextjs/src/config/withSentryConfig.ts | 74 ++++++++++++++++++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/packages/nextjs/src/config/getBuildPluginOptions.ts b/packages/nextjs/src/config/getBuildPluginOptions.ts index e36e88802fa5..62f8a85bb0a6 100644 --- a/packages/nextjs/src/config/getBuildPluginOptions.ts +++ b/packages/nextjs/src/config/getBuildPluginOptions.ts @@ -205,7 +205,7 @@ function createReleaseConfig( vcsRemote: sentryBuildOptions.release?.vcsRemote, setCommits: sentryBuildOptions.release?.setCommits, deploy: sentryBuildOptions.release?.deploy, - ...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.release, + ...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.release, }; } @@ -241,7 +241,7 @@ export function getBuildPluginOptions({ const normalizedDistDirAbsPath = normalizePathForGlob(distDirAbsPath); const loggerPrefix = LOGGER_PREFIXES[buildTool]; - const widenClientFileUpload = sentryBuildOptions.widenClientFileUpload ?? false; + const widenClientFileUpload = sentryBuildOptions.webpack?.widenClientFileUpload ?? false; const deleteSourcemapsAfterUpload = sentryBuildOptions.sourcemaps?.deleteSourcemapsAfterUpload ?? false; const sourcemapUploadAssets = createSourcemapUploadAssetPatterns( @@ -272,8 +272,8 @@ export function getBuildPluginOptions({ reactComponentAnnotation: buildTool.startsWith('after-production-compile') ? undefined : { - ...sentryBuildOptions.reactComponentAnnotation, - ...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.reactComponentAnnotation, + ...sentryBuildOptions.webpack?.reactComponentAnnotation, + ...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.reactComponentAnnotation, }, silent: sentryBuildOptions.silent, url: sentryBuildOptions.sentryUrl, @@ -283,7 +283,7 @@ export function getBuildPluginOptions({ assets: sentryBuildOptions.sourcemaps?.assets ?? sourcemapUploadAssets, ignore: sentryBuildOptions.sourcemaps?.ignore ?? sourcemapUploadIgnore, filesToDeleteAfterUpload, - ...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.sourcemaps, + ...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.sourcemaps, }, release: createReleaseConfig(releaseName, sentryBuildOptions), bundleSizeOptimizations: { @@ -295,6 +295,6 @@ export function getBuildPluginOptions({ metaFramework: 'nextjs', }, }, - ...sentryBuildOptions.unstable_sentryWebpackPluginOptions, + ...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions, }; } diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index df32c31f392e..0ce5a3235523 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -145,7 +145,7 @@ export function constructWebpackConfigFunction({ appDir: appDirPath, pagesDir: pagesDirPath, pageExtensionRegex, - excludeServerRoutes: userSentryOptions.excludeServerRoutes, + excludeServerRoutes: userSentryOptions.webpack?.excludeServerRoutes, nextjsRequestAsyncStorageModulePath: getRequestAsyncStorageModuleLocation( projectDir, rawNewConfig.resolve?.modules, @@ -220,7 +220,7 @@ export function constructWebpackConfigFunction({ ); }; - if (isServer && userSentryOptions.autoInstrumentServerFunctions !== false) { + if (isServer && userSentryOptions.webpack?.autoInstrumentServerFunctions !== false) { // It is very important that we insert our loaders at the beginning of the array because we expect any sort of transformations/transpilations (e.g. TS -> JS) to already have happened. // Wrap pages @@ -239,7 +239,7 @@ export function constructWebpackConfigFunction({ let vercelCronsConfig: VercelCronsConfig = undefined; try { - if (process.env.VERCEL && userSentryOptions.automaticVercelMonitors) { + if (process.env.VERCEL && userSentryOptions.webpack?.automaticVercelMonitors) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access vercelCronsConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'vercel.json'), 'utf8')).crons; if (vercelCronsConfig) { @@ -277,7 +277,7 @@ export function constructWebpackConfigFunction({ // Wrap middleware const canWrapStandaloneMiddleware = userNextConfig.output !== 'standalone' || !major || major < 16; - if ((userSentryOptions.autoInstrumentMiddleware ?? true) && canWrapStandaloneMiddleware) { + if ((userSentryOptions.webpack?.autoInstrumentMiddleware ?? true) && canWrapStandaloneMiddleware) { newConfig.module.rules.unshift({ test: isMiddlewareResource, use: [ @@ -293,7 +293,7 @@ export function constructWebpackConfigFunction({ } } - if (isServer && userSentryOptions.autoInstrumentAppDirectory !== false) { + if (isServer && userSentryOptions.webpack?.autoInstrumentAppDirectory !== false) { // Wrap server components newConfig.module.rules.unshift({ test: isServerComponentResource, @@ -431,7 +431,7 @@ export function constructWebpackConfigFunction({ } } - if (userSentryOptions.disableLogger) { + if (userSentryOptions.webpack?.treeshake?.debugLogs) { newConfig.plugins = newConfig.plugins || []; newConfig.plugins.push( new buildContext.webpack.DefinePlugin({ diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index 892f4d6745fa..bab87e8085dd 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -98,12 +98,84 @@ function generateRandomTunnelRoute(): string { return `/${randomString}`; } +/** + * Migrates deprecated top-level webpack options to the new `webpack.*` path for backward compatibility. + * The new path takes precedence over deprecated options. This mutates the userSentryOptions object. + */ +function migrateDeprecatedWebpackOptions(userSentryOptions: SentryBuildOptions): void { + // Initialize webpack options if not present + userSentryOptions.webpack = userSentryOptions.webpack || {}; + + const webpack = userSentryOptions.webpack; + + const withDeprecatedFallback = (newValue: T | undefined, deprecatedValue: T | undefined): T | undefined => { + return newValue ?? deprecatedValue; + }; + + /* eslint-disable deprecation/deprecation */ + // Migrate each deprecated option to the new path, but only if the new path isn't already set + webpack.autoInstrumentServerFunctions = withDeprecatedFallback( + webpack.autoInstrumentServerFunctions, + userSentryOptions.autoInstrumentServerFunctions, + ); + + webpack.autoInstrumentMiddleware = withDeprecatedFallback( + webpack.autoInstrumentMiddleware, + userSentryOptions.autoInstrumentMiddleware, + ); + + webpack.autoInstrumentAppDirectory = withDeprecatedFallback( + webpack.autoInstrumentAppDirectory, + userSentryOptions.autoInstrumentAppDirectory, + ); + + webpack.excludeServerRoutes = withDeprecatedFallback( + webpack.excludeServerRoutes, + userSentryOptions.excludeServerRoutes, + ); + + webpack.widenClientFileUpload = withDeprecatedFallback( + webpack.widenClientFileUpload, + userSentryOptions.widenClientFileUpload, + ); + + webpack.unstable_sentryWebpackPluginOptions = withDeprecatedFallback( + webpack.unstable_sentryWebpackPluginOptions, + userSentryOptions.unstable_sentryWebpackPluginOptions, + ); + + webpack.disableSentryConfig = withDeprecatedFallback( + webpack.disableSentryConfig, + userSentryOptions.disableSentryWebpackConfig, + ); + + // Handle treeshake.debugLogs specially since it's nested + if (userSentryOptions.disableLogger !== undefined) { + webpack.treeshake = webpack.treeshake || {}; + webpack.treeshake.debugLogs = withDeprecatedFallback(webpack.treeshake.debugLogs, userSentryOptions.disableLogger); + } + + webpack.automaticVercelMonitors = withDeprecatedFallback( + webpack.automaticVercelMonitors, + userSentryOptions.automaticVercelMonitors, + ); + + webpack.reactComponentAnnotation = withDeprecatedFallback( + webpack.reactComponentAnnotation, + userSentryOptions.reactComponentAnnotation, + ); + /* eslint-enable deprecation/deprecation */ +} + // Modify the materialized object form of the user's next config by deleting the `sentry` property and wrapping the // `webpack` property function getFinalConfigObject( incomingUserNextConfigObject: NextConfigObject, userSentryOptions: SentryBuildOptions, ): NextConfigObject { + // Migrate deprecated webpack options to new webpack path for backward compatibility + migrateDeprecatedWebpackOptions(userSentryOptions); + // Only determine a release name if release creation is not explicitly disabled // This prevents injection of Git commit hashes that break build determinism const shouldCreateRelease = userSentryOptions.release?.create !== false; @@ -363,7 +435,7 @@ function getFinalConfigObject( ], }, }), - ...(isWebpack && !userSentryOptions.disableSentryWebpackConfig + ...(isWebpack && !userSentryOptions.webpack?.disableSentryConfig ? { webpack: constructWebpackConfigFunction({ userNextConfig: incomingUserNextConfigObject, From c5ffac908742adaa2fd47cab81f0455ef00248d0 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 27 Nov 2025 12:44:45 +0200 Subject: [PATCH 3/6] test: added a test --- .../test/config/withSentryConfig.test.ts | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index b67a05845a7e..c18aa6898913 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -267,6 +267,98 @@ describe('withSentryConfig', () => { expect(finalConfig.turbopack).toBeUndefined(); }); + + describe('webpack configuration options path', () => { + afterEach(() => { + delete process.env.TURBOPACK; + vi.restoreAllMocks(); + }); + + it('uses new webpack.disableSentryConfig option', () => { + delete process.env.TURBOPACK; + + const originalWebpackFunction = vi.fn(); + const configWithWebpack = { + ...exportedNextConfig, + webpack: originalWebpackFunction, + }; + + const sentryOptions = { + webpack: { + disableSentryConfig: true, + }, + }; + + const finalConfig = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptions); + expect(finalConfig.webpack).toBe(originalWebpackFunction); + }); + + it('new webpack path takes precedence over deprecated top-level options', () => { + delete process.env.TURBOPACK; + + const originalWebpackFunction = vi.fn(); + const configWithWebpack = { + ...exportedNextConfig, + webpack: originalWebpackFunction, + }; + + // Both old and new paths set, new should win + const sentryOptions = { + disableSentryWebpackConfig: false, // deprecated - says enable + webpack: { + disableSentryConfig: true, // new - says disable + }, + }; + + const finalConfig = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptions); + // Should preserve original webpack because new path disables it + expect(finalConfig.webpack).toBe(originalWebpackFunction); + }); + + it('falls back to deprecated option when new path is not set', () => { + delete process.env.TURBOPACK; + + const originalWebpackFunction = vi.fn(); + const configWithWebpack = { + ...exportedNextConfig, + webpack: originalWebpackFunction, + }; + + // Only deprecated path set + const sentryOptions = { + disableSentryWebpackConfig: true, + }; + + const finalConfig = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptions); + // Should preserve original webpack because deprecated option disables it + expect(finalConfig.webpack).toBe(originalWebpackFunction); + }); + + it('merges webpack.treeshake.debugLogs with deprecated disableLogger', () => { + delete process.env.TURBOPACK; + + // New webpack.treeshake.debugLogs should map to disableLogger internally + const sentryOptionsNew = { + webpack: { + treeshake: { + debugLogs: true, + }, + }, + }; + + const sentryOptionsOld = { + disableLogger: true, + }; + + // Both should work the same way internally (though we can't easily test the actual effect here) + const finalConfigNew = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptionsNew); + const finalConfigOld = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptionsOld); + + // Both should have webpack functions (not disabled) + expect(finalConfigNew.webpack).toBeInstanceOf(Function); + expect(finalConfigOld.webpack).toBeInstanceOf(Function); + }); + }); }); describe('bundler detection', () => { From 55f8d3bd400f7a2b573050e695bfd840e7e9b15b Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 27 Nov 2025 13:02:17 +0200 Subject: [PATCH 4/6] feat: add warnings when deprecated values are used --- packages/nextjs/src/config/types.ts | 2 -- .../nextjs/src/config/withSentryConfig.ts | 30 +++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index db587cba0983..ed036814d379 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -141,8 +141,6 @@ export type SentryBuildWebpackOptions = { * When enabled, your app's DOM will automatically be annotated during build-time with their respective component names. * This will unlock the capability to search for Replays in Sentry by component name, as well as see component names in breadcrumbs and performance monitoring. * Please note that this feature is not currently supported by the esbuild bundler plugins, and will only annotate React components - * - * @deprecated Use `webpack.reactComponentAnnotation` instead. */ reactComponentAnnotation?: { /** diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index bab87e8085dd..99ab019c5bd5 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -108,63 +108,87 @@ function migrateDeprecatedWebpackOptions(userSentryOptions: SentryBuildOptions): const webpack = userSentryOptions.webpack; - const withDeprecatedFallback = (newValue: T | undefined, deprecatedValue: T | undefined): T | undefined => { + const withDeprecatedFallback = ( + newValue: T | undefined, + deprecatedValue: T | undefined, + message: string, + ): T | undefined => { + if (deprecatedValue !== undefined && deprecatedValue !== newValue) { + // eslint-disable-next-line no-console + console.warn(message); + } + return newValue ?? deprecatedValue; }; + const deprecatedMessage = (deprecatedPath: string, newPath: string): string => + `[@sentry/nextjs] DEPRECATION WARNING: ${deprecatedPath} is deprecated and will be removed in a future version. Use ${newPath} instead.`; + /* eslint-disable deprecation/deprecation */ // Migrate each deprecated option to the new path, but only if the new path isn't already set webpack.autoInstrumentServerFunctions = withDeprecatedFallback( webpack.autoInstrumentServerFunctions, userSentryOptions.autoInstrumentServerFunctions, + deprecatedMessage('autoInstrumentServerFunctions', 'webpack.autoInstrumentServerFunctions'), ); webpack.autoInstrumentMiddleware = withDeprecatedFallback( webpack.autoInstrumentMiddleware, userSentryOptions.autoInstrumentMiddleware, + deprecatedMessage('autoInstrumentMiddleware', 'webpack.autoInstrumentMiddleware'), ); webpack.autoInstrumentAppDirectory = withDeprecatedFallback( webpack.autoInstrumentAppDirectory, userSentryOptions.autoInstrumentAppDirectory, + deprecatedMessage('autoInstrumentAppDirectory', 'webpack.autoInstrumentAppDirectory'), ); webpack.excludeServerRoutes = withDeprecatedFallback( webpack.excludeServerRoutes, userSentryOptions.excludeServerRoutes, + deprecatedMessage('excludeServerRoutes', 'webpack.excludeServerRoutes'), ); webpack.widenClientFileUpload = withDeprecatedFallback( webpack.widenClientFileUpload, userSentryOptions.widenClientFileUpload, + deprecatedMessage('widenClientFileUpload', 'webpack.widenClientFileUpload'), ); webpack.unstable_sentryWebpackPluginOptions = withDeprecatedFallback( webpack.unstable_sentryWebpackPluginOptions, userSentryOptions.unstable_sentryWebpackPluginOptions, + deprecatedMessage('unstable_sentryWebpackPluginOptions', 'webpack.unstable_sentryWebpackPluginOptions'), ); webpack.disableSentryConfig = withDeprecatedFallback( webpack.disableSentryConfig, userSentryOptions.disableSentryWebpackConfig, + deprecatedMessage('disableSentryWebpackConfig', 'webpack.disableSentryConfig'), ); // Handle treeshake.debugLogs specially since it's nested if (userSentryOptions.disableLogger !== undefined) { webpack.treeshake = webpack.treeshake || {}; - webpack.treeshake.debugLogs = withDeprecatedFallback(webpack.treeshake.debugLogs, userSentryOptions.disableLogger); + webpack.treeshake.debugLogs = withDeprecatedFallback( + webpack.treeshake.debugLogs, + userSentryOptions.disableLogger, + deprecatedMessage('disableLogger', 'webpack.treeshake.debugLogs'), + ); } webpack.automaticVercelMonitors = withDeprecatedFallback( webpack.automaticVercelMonitors, userSentryOptions.automaticVercelMonitors, + deprecatedMessage('automaticVercelMonitors', 'webpack.automaticVercelMonitors'), ); webpack.reactComponentAnnotation = withDeprecatedFallback( webpack.reactComponentAnnotation, userSentryOptions.reactComponentAnnotation, + deprecatedMessage('reactComponentAnnotation', 'webpack.reactComponentAnnotation'), ); - /* eslint-enable deprecation/deprecation */ } // Modify the materialized object form of the user's next config by deleting the `sentry` property and wrapping the From 444c77817192f85ebfec1dd3f3ae95aa27f94ebe Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 27 Nov 2025 13:09:10 +0200 Subject: [PATCH 5/6] fix: simplify deprecated value warning condition --- packages/nextjs/src/config/withSentryConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index 99ab019c5bd5..bca654d855d0 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -113,7 +113,7 @@ function migrateDeprecatedWebpackOptions(userSentryOptions: SentryBuildOptions): deprecatedValue: T | undefined, message: string, ): T | undefined => { - if (deprecatedValue !== undefined && deprecatedValue !== newValue) { + if (deprecatedValue !== undefined) { // eslint-disable-next-line no-console console.warn(message); } From 9ef882ca189160936217cc35b0479ba41afd8fbd Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 27 Nov 2025 13:09:18 +0200 Subject: [PATCH 6/6] tests: update Sentry options structure to use webpack namespace and add deprecation warnings for top-level options --- .../test/config/getBuildPluginOptions.test.ts | 44 +++++---- .../test/config/withSentryConfig.test.ts | 97 +++++++++++++++++++ 2 files changed, 123 insertions(+), 18 deletions(-) diff --git a/packages/nextjs/test/config/getBuildPluginOptions.test.ts b/packages/nextjs/test/config/getBuildPluginOptions.test.ts index 609183d198bb..d035c483fd9c 100644 --- a/packages/nextjs/test/config/getBuildPluginOptions.test.ts +++ b/packages/nextjs/test/config/getBuildPluginOptions.test.ts @@ -129,7 +129,9 @@ describe('getBuildPluginOptions', () => { const result = getBuildPluginOptions({ sentryBuildOptions: { ...baseSentryOptions, - widenClientFileUpload: true, + webpack: { + widenClientFileUpload: true, + }, }, releaseName: mockReleaseName, distDirAbsPath: mockDistDirAbsPath, @@ -573,13 +575,15 @@ describe('getBuildPluginOptions', () => { create: true, vcsRemote: 'origin', }, - unstable_sentryWebpackPluginOptions: { - release: { - setCommits: { - auto: true, - }, - deploy: { - env: 'production', + webpack: { + unstable_sentryWebpackPluginOptions: { + release: { + setCommits: { + auto: true, + }, + deploy: { + env: 'production', + }, }, }, }, @@ -592,7 +596,7 @@ describe('getBuildPluginOptions', () => { buildTool: 'webpack-client', }); - // The unstable_sentryWebpackPluginOptions.release is spread at the end and may override base properties + // The webpack.unstable_sentryWebpackPluginOptions.release is spread at the end and may override base properties expect(result.release).toHaveProperty('setCommits.auto', true); expect(result.release).toHaveProperty('deploy.env', 'production'); }); @@ -603,12 +607,14 @@ describe('getBuildPluginOptions', () => { const sentryBuildOptions: SentryBuildOptions = { org: 'test-org', project: 'test-project', - reactComponentAnnotation: { - enabled: true, - }, - unstable_sentryWebpackPluginOptions: { + webpack: { reactComponentAnnotation: { - enabled: false, // This will override the base setting + enabled: true, + }, + unstable_sentryWebpackPluginOptions: { + reactComponentAnnotation: { + enabled: false, // This will override the base setting + }, }, }, }; @@ -695,10 +701,12 @@ describe('getBuildPluginOptions', () => { const sentryBuildOptions: SentryBuildOptions = { org: 'test-org', project: 'test-project', - unstable_sentryWebpackPluginOptions: { - applicationKey: 'test-app-key', - sourcemaps: { - disable: false, + webpack: { + unstable_sentryWebpackPluginOptions: { + applicationKey: 'test-app-key', + sourcemaps: { + disable: false, + }, }, }, }; diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index c18aa6898913..8ecf221f042a 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -359,6 +359,103 @@ describe('withSentryConfig', () => { expect(finalConfigOld.webpack).toBeInstanceOf(Function); }); }); + + describe('deprecation warnings', () => { + let consoleWarnSpy: ReturnType; + + beforeEach(() => { + consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleWarnSpy.mockRestore(); + delete process.env.TURBOPACK; + vi.restoreAllMocks(); + }); + + it('warns when using deprecated top-level options', () => { + delete process.env.TURBOPACK; + + const sentryOptions = { + disableLogger: true, + widenClientFileUpload: true, + }; + + materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('[@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated'), + ); + expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Use webpack.treeshake.debugLogs instead')); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('[@sentry/nextjs] DEPRECATION WARNING: widenClientFileUpload is deprecated'), + ); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('Use webpack.widenClientFileUpload instead'), + ); + }); + + it('does not warn when using new webpack path', () => { + delete process.env.TURBOPACK; + + const sentryOptions = { + webpack: { + treeshake: { + debugLogs: true, + }, + widenClientFileUpload: true, + }, + }; + + materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + + it('warns even when new path is also set', () => { + delete process.env.TURBOPACK; + + const sentryOptions = { + disableLogger: true, // deprecated + webpack: { + treeshake: { + debugLogs: false, // new path takes precedence + }, + }, + }; + + materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + + // Should warn because deprecated value is present + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('[@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated'), + ); + }); + + it('warns for multiple deprecated options at once', () => { + delete process.env.TURBOPACK; + + const sentryOptions = { + disableLogger: true, + automaticVercelMonitors: false, + excludeServerRoutes: ['/api/test'], + }; + + materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + + // Should warn for all three deprecated options + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('[@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated'), + ); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('[@sentry/nextjs] DEPRECATION WARNING: automaticVercelMonitors is deprecated'), + ); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('[@sentry/nextjs] DEPRECATION WARNING: excludeServerRoutes is deprecated'), + ); + expect(consoleWarnSpy).toHaveBeenCalledTimes(3); + }); + }); }); describe('bundler detection', () => {