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 f60a7cdbe7c512954c863502c059dfe87de50b69 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Sun, 21 Sep 2025 00:09:29 +0200 Subject: [PATCH 2/2] fix tests --- .../test/config/withSentryConfig.test.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index f98e0d6d86de..5121cb51d6fd 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -776,7 +776,11 @@ describe('withSentryConfig', () => { useRunAfterProductionCompileHook: true, }; - const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + // Use a clean copy of the config to avoid test interference + const cleanConfig = { ...exportedNextConfig }; + delete cleanConfig.compiler; + + const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); }); @@ -878,12 +882,14 @@ describe('withSentryConfig', () => { vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; - const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + // Use a clean copy of the config to avoid test interference + const cleanConfig = { ...exportedNextConfig }; + delete cleanConfig.compiler; + + const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); @@ -895,12 +901,14 @@ describe('withSentryConfig', () => { vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); const sentryOptions = { - _experimental: { - useRunAfterProductionCompileHook: true, - }, + useRunAfterProductionCompileHook: true, }; - const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions); + // Use a clean copy of the config to avoid test interference + const cleanConfig = { ...exportedNextConfig }; + delete cleanConfig.compiler; + + const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions); expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function); });