From e90bcdec4a46d942ba379e6a2dfe29c94e402b57 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Sat, 20 Sep 2025 23:06:22 +0200 Subject: [PATCH 1/2] promote flag to non-experimental --- packages/nextjs/src/config/types.ts | 21 +-- .../nextjs/src/config/withSentryConfig.ts | 4 +- packages/nextjs/test/config/testUtils.ts | 3 +- .../webpack/constructWebpackConfig.test.ts | 8 +- .../test/config/withSentryConfig.test.ts | 123 ++---------------- 5 files changed, 24 insertions(+), 135 deletions(-) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 55f080fb433e..23b18f39b253 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -501,22 +501,23 @@ export type SentryBuildOptions = { */ disableSentryWebpackConfig?: boolean; + /** + * When true (and Next.js >= 15), use the runAfterProductionCompile hook to consolidate sourcemap uploads + * into a single operation after builds complete, reducing build time. + * + * When false, use the traditional approach of uploading sourcemaps during each webpack build. For Turbopack no sourcemaps will be uploaded. + * + * @default false + */ + useRunAfterProductionCompileHook?: boolean; + /** * Contains a set of experimental flags that might change in future releases. These flags enable * features that are still in development and may be modified, renamed, or removed without notice. * Use with caution in production environments. */ _experimental?: Partial<{ - /** - * When true (and Next.js >= 15), use the runAfterProductionCompile hook to consolidate sourcemap uploads - * into a single operation after turbopack builds complete, reducing build time. - * - * When false, use the traditional approach of uploading sourcemaps during each webpack build. - * - * @default false - */ - useRunAfterProductionCompileHook?: boolean; - thirdPartyOriginStackFrames: boolean; + thirdPartyOriginStackFrames?: boolean; }>; }; diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index 494052af26f2..e27f2bf37e25 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -294,7 +294,7 @@ function getFinalConfigObject( } } - if (userSentryOptions?._experimental?.useRunAfterProductionCompileHook === true && supportsProductionCompileHook()) { + if (userSentryOptions?.useRunAfterProductionCompileHook === true && supportsProductionCompileHook()) { if (incomingUserNextConfigObject?.compiler?.runAfterProductionCompile === undefined) { incomingUserNextConfigObject.compiler ??= {}; incomingUserNextConfigObject.compiler.runAfterProductionCompile = async ({ distDir }) => { @@ -379,7 +379,7 @@ function getFinalConfigObject( releaseName, routeManifest, nextJsVersion, - useRunAfterProductionCompileHook: userSentryOptions._experimental?.useRunAfterProductionCompileHook, + useRunAfterProductionCompileHook: userSentryOptions?.useRunAfterProductionCompileHook, }), ...(isTurbopackSupported && isTurbopack ? { diff --git a/packages/nextjs/test/config/testUtils.ts b/packages/nextjs/test/config/testUtils.ts index a644525ce311..c3769dfdf00f 100644 --- a/packages/nextjs/test/config/testUtils.ts +++ b/packages/nextjs/test/config/testUtils.ts @@ -77,8 +77,7 @@ export async function materializeFinalWebpackConfig(options: { routeManifest: options.routeManifest, nextJsVersion: options.nextJsVersion, useRunAfterProductionCompileHook: - options.useRunAfterProductionCompileHook ?? - options.sentryBuildTimeOptions?._experimental?.useRunAfterProductionCompileHook, + options.useRunAfterProductionCompileHook ?? options.sentryBuildTimeOptions?.useRunAfterProductionCompileHook, }); // call it to get concrete values for comparison diff --git a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts index d46bcd917fb7..b8cfb4015512 100644 --- a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts +++ b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts @@ -100,9 +100,7 @@ describe('constructWebpackConfigFunction()', () => { incomingWebpackConfig: serverWebpackConfig, incomingWebpackBuildContext: serverBuildContext, sentryBuildTimeOptions: { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }, }); @@ -128,9 +126,7 @@ describe('constructWebpackConfigFunction()', () => { incomingWebpackConfig: serverWebpackConfig, incomingWebpackBuildContext: serverBuildContext, sentryBuildTimeOptions: { - _experimental: { - useRunAfterProductionCompileHook: false, - }, + useRunAfterProductionCompileHook: false, }, }); diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index d0b30aa7eae3..f98e0d6d86de 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -769,13 +769,11 @@ describe('withSentryConfig', () => { vi.restoreAllMocks(); }); - it('sets up runAfterProductionCompile hook when experimental flag is enabled and version is supported', () => { + it('sets up runAfterProductionCompile hook when flag is enabled and version is supported', () => { vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); @@ -783,13 +781,11 @@ describe('withSentryConfig', () => { expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); }); - it('does not set up hook when experimental flag is disabled', () => { + it('does not set up hook when flag is disabled', () => { vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: false, - }, + useRunAfterProductionCompileHook: false, }; const cleanConfig = { ...exportedNextConfig }; @@ -804,9 +800,7 @@ describe('withSentryConfig', () => { vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(false); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; const cleanConfig = { ...exportedNextConfig }; @@ -829,9 +823,7 @@ describe('withSentryConfig', () => { }; const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; const finalConfig = materializeFinalNextConfig(configWithExistingHook, undefined, sentryOptions); @@ -852,9 +844,7 @@ describe('withSentryConfig', () => { }; const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; materializeFinalNextConfig(configWithInvalidHook, undefined, sentryOptions); @@ -873,9 +863,7 @@ describe('withSentryConfig', () => { delete configWithoutCompiler.compiler; const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; const finalConfig = materializeFinalNextConfig(configWithoutCompiler, undefined, sentryOptions); @@ -917,99 +905,4 @@ describe('withSentryConfig', () => { expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); }); }); - - describe('experimental flag handling', () => { - afterEach(() => { - vi.restoreAllMocks(); - }); - - it('respects useRunAfterProductionCompileHook: true', () => { - vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); - - const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, - }; - - const cleanConfig = { ...exportedNextConfig }; - delete cleanConfig.compiler; - - const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); - - expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); - }); - - it('respects useRunAfterProductionCompileHook: false', () => { - vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); - - const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: false, - }, - }; - - const cleanConfig = { ...exportedNextConfig }; - delete cleanConfig.compiler; - - const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); - - expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined(); - }); - - it('does not set up hook when experimental flag is undefined', () => { - vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); - - const sentryOptions = { - _experimental: { - // useRunAfterProductionCompileHook not specified - }, - }; - - const cleanConfig = { ...exportedNextConfig }; - delete cleanConfig.compiler; - - const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); - - expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined(); - }); - - it('does not set up hook when _experimental is undefined', () => { - vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); - - const sentryOptions = { - // no _experimental property - }; - - const cleanConfig = { ...exportedNextConfig }; - delete cleanConfig.compiler; - - const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); - - expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined(); - }); - - it('combines experimental flag with other configurations correctly', () => { - process.env.TURBOPACK = '1'; - vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.4.1'); - vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); - - const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, - sourcemaps: {}, - tunnelRoute: '/tunnel', - }; - - const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); - - // Should have both turbopack sourcemap config AND runAfterProductionCompile hook - expect(finalConfig.productionBrowserSourceMaps).toBe(true); - expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); - expect(finalConfig.rewrites).toBeInstanceOf(Function); - - delete process.env.TURBOPACK; - }); - }); }); From 9b5f828e08bb383943b2518ced55172e729c2342 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Sat, 20 Sep 2025 23:27:13 +0200 Subject: [PATCH 2/2] flip default value for turbopack --- packages/nextjs/src/config/types.ts | 2 +- .../nextjs/src/config/withSentryConfig.ts | 8 ++- .../test/config/withSentryConfig.test.ts | 68 +++++++++++++++++-- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 23b18f39b253..1fa245412f2c 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -507,7 +507,7 @@ export type SentryBuildOptions = { * * When false, use the traditional approach of uploading sourcemaps during each webpack build. For Turbopack no sourcemaps will be uploaded. * - * @default false + * @default true for Turbopack, false for Webpack */ useRunAfterProductionCompileHook?: boolean; diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index e27f2bf37e25..b5c2be2f25bb 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -294,7 +294,11 @@ function getFinalConfigObject( } } - if (userSentryOptions?.useRunAfterProductionCompileHook === true && supportsProductionCompileHook()) { + // If not explicitly set, turbopack uses the runAfterProductionCompile hook (as there are no alternatives), webpack does not. + const shouldUseRunAfterProductionCompileHook = + userSentryOptions?.useRunAfterProductionCompileHook ?? (isTurbopack ? true : false); + + if (shouldUseRunAfterProductionCompileHook && supportsProductionCompileHook()) { if (incomingUserNextConfigObject?.compiler?.runAfterProductionCompile === undefined) { incomingUserNextConfigObject.compiler ??= {}; incomingUserNextConfigObject.compiler.runAfterProductionCompile = async ({ distDir }) => { @@ -379,7 +383,7 @@ function getFinalConfigObject( releaseName, routeManifest, nextJsVersion, - useRunAfterProductionCompileHook: userSentryOptions?.useRunAfterProductionCompileHook, + useRunAfterProductionCompileHook: shouldUseRunAfterProductionCompileHook, }), ...(isTurbopackSupported && isTurbopack ? { diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index f98e0d6d86de..b707da349cef 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -872,15 +872,73 @@ describe('withSentryConfig', () => { expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); }); + it('defaults to true for turbopack when useRunAfterProductionCompileHook is not specified', () => { + process.env.TURBOPACK = '1'; + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.4.1'); + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); + + const sentryOptions = {}; // No useRunAfterProductionCompileHook specified + + const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + + expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); + + delete process.env.TURBOPACK; + }); + + it('defaults to false for webpack when useRunAfterProductionCompileHook is not specified', () => { + delete process.env.TURBOPACK; + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); + + const sentryOptions = {}; // No useRunAfterProductionCompileHook specified + + const cleanConfig = { ...exportedNextConfig }; + delete cleanConfig.compiler; + + const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); + + expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined(); + }); + + it('respects explicit false setting for turbopack', () => { + process.env.TURBOPACK = '1'; + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.4.1'); + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); + + const sentryOptions = { + useRunAfterProductionCompileHook: false, + }; + + const cleanConfig = { ...exportedNextConfig }; + delete cleanConfig.compiler; + + const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); + + expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined(); + + delete process.env.TURBOPACK; + }); + + it('respects explicit true setting for webpack', () => { + delete process.env.TURBOPACK; + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); + + const sentryOptions = { + useRunAfterProductionCompileHook: true, + }; + + const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + + expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); + }); + it('works with turbopack builds when TURBOPACK env is set', () => { process.env.TURBOPACK = '1'; vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.4.1'); vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); @@ -895,9 +953,7 @@ describe('withSentryConfig', () => { vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);