Skip to content

Commit cf7913c

Browse files
authored
feat(nextjs): Promote useRunAfterProductionCompileHook to non-experimental build option (#17721)
1 parent 9e24a70 commit cf7913c

File tree

5 files changed

+36
-139
lines changed

5 files changed

+36
-139
lines changed

packages/nextjs/src/config/types.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -501,22 +501,23 @@ export type SentryBuildOptions = {
501501
*/
502502
disableSentryWebpackConfig?: boolean;
503503

504+
/**
505+
* When true (and Next.js >= 15), use the runAfterProductionCompile hook to consolidate sourcemap uploads
506+
* into a single operation after builds complete, reducing build time.
507+
*
508+
* When false, use the traditional approach of uploading sourcemaps during each webpack build. For Turbopack no sourcemaps will be uploaded.
509+
*
510+
* @default false
511+
*/
512+
useRunAfterProductionCompileHook?: boolean;
513+
504514
/**
505515
* Contains a set of experimental flags that might change in future releases. These flags enable
506516
* features that are still in development and may be modified, renamed, or removed without notice.
507517
* Use with caution in production environments.
508518
*/
509519
_experimental?: Partial<{
510-
/**
511-
* When true (and Next.js >= 15), use the runAfterProductionCompile hook to consolidate sourcemap uploads
512-
* into a single operation after turbopack builds complete, reducing build time.
513-
*
514-
* When false, use the traditional approach of uploading sourcemaps during each webpack build.
515-
*
516-
* @default false
517-
*/
518-
useRunAfterProductionCompileHook?: boolean;
519-
thirdPartyOriginStackFrames: boolean;
520+
thirdPartyOriginStackFrames?: boolean;
520521
}>;
521522
};
522523

packages/nextjs/src/config/withSentryConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ function getFinalConfigObject(
294294
}
295295
}
296296

297-
if (userSentryOptions?._experimental?.useRunAfterProductionCompileHook === true && supportsProductionCompileHook()) {
297+
if (userSentryOptions?.useRunAfterProductionCompileHook === true && supportsProductionCompileHook()) {
298298
if (incomingUserNextConfigObject?.compiler?.runAfterProductionCompile === undefined) {
299299
incomingUserNextConfigObject.compiler ??= {};
300300
incomingUserNextConfigObject.compiler.runAfterProductionCompile = async ({ distDir }) => {
@@ -379,7 +379,7 @@ function getFinalConfigObject(
379379
releaseName,
380380
routeManifest,
381381
nextJsVersion,
382-
useRunAfterProductionCompileHook: userSentryOptions._experimental?.useRunAfterProductionCompileHook,
382+
useRunAfterProductionCompileHook: userSentryOptions?.useRunAfterProductionCompileHook,
383383
}),
384384
...(isTurbopackSupported && isTurbopack
385385
? {

packages/nextjs/test/config/testUtils.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ export async function materializeFinalWebpackConfig(options: {
7777
routeManifest: options.routeManifest,
7878
nextJsVersion: options.nextJsVersion,
7979
useRunAfterProductionCompileHook:
80-
options.useRunAfterProductionCompileHook ??
81-
options.sentryBuildTimeOptions?._experimental?.useRunAfterProductionCompileHook,
80+
options.useRunAfterProductionCompileHook ?? options.sentryBuildTimeOptions?.useRunAfterProductionCompileHook,
8281
});
8382

8483
// call it to get concrete values for comparison

packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,7 @@ describe('constructWebpackConfigFunction()', () => {
100100
incomingWebpackConfig: serverWebpackConfig,
101101
incomingWebpackBuildContext: serverBuildContext,
102102
sentryBuildTimeOptions: {
103-
_experimental: {
104-
useRunAfterProductionCompileHook: true,
105-
},
103+
useRunAfterProductionCompileHook: true,
106104
},
107105
});
108106

@@ -128,9 +126,7 @@ describe('constructWebpackConfigFunction()', () => {
128126
incomingWebpackConfig: serverWebpackConfig,
129127
incomingWebpackBuildContext: serverBuildContext,
130128
sentryBuildTimeOptions: {
131-
_experimental: {
132-
useRunAfterProductionCompileHook: false,
133-
},
129+
useRunAfterProductionCompileHook: false,
134130
},
135131
});
136132

packages/nextjs/test/config/withSentryConfig.test.ts

Lines changed: 20 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -769,27 +769,27 @@ describe('withSentryConfig', () => {
769769
vi.restoreAllMocks();
770770
});
771771

772-
it('sets up runAfterProductionCompile hook when experimental flag is enabled and version is supported', () => {
772+
it('sets up runAfterProductionCompile hook when flag is enabled and version is supported', () => {
773773
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
774774

775775
const sentryOptions = {
776-
_experimental: {
777-
useRunAfterProductionCompileHook: true,
778-
},
776+
useRunAfterProductionCompileHook: true,
779777
};
780778

781-
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
779+
// Use a clean copy of the config to avoid test interference
780+
const cleanConfig = { ...exportedNextConfig };
781+
delete cleanConfig.compiler;
782+
783+
const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions);
782784

783785
expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function);
784786
});
785787

786-
it('does not set up hook when experimental flag is disabled', () => {
788+
it('does not set up hook when flag is disabled', () => {
787789
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
788790

789791
const sentryOptions = {
790-
_experimental: {
791-
useRunAfterProductionCompileHook: false,
792-
},
792+
useRunAfterProductionCompileHook: false,
793793
};
794794

795795
const cleanConfig = { ...exportedNextConfig };
@@ -804,9 +804,7 @@ describe('withSentryConfig', () => {
804804
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(false);
805805

806806
const sentryOptions = {
807-
_experimental: {
808-
useRunAfterProductionCompileHook: true,
809-
},
807+
useRunAfterProductionCompileHook: true,
810808
};
811809

812810
const cleanConfig = { ...exportedNextConfig };
@@ -829,9 +827,7 @@ describe('withSentryConfig', () => {
829827
};
830828

831829
const sentryOptions = {
832-
_experimental: {
833-
useRunAfterProductionCompileHook: true,
834-
},
830+
useRunAfterProductionCompileHook: true,
835831
};
836832

837833
const finalConfig = materializeFinalNextConfig(configWithExistingHook, undefined, sentryOptions);
@@ -852,9 +848,7 @@ describe('withSentryConfig', () => {
852848
};
853849

854850
const sentryOptions = {
855-
_experimental: {
856-
useRunAfterProductionCompileHook: true,
857-
},
851+
useRunAfterProductionCompileHook: true,
858852
};
859853

860854
materializeFinalNextConfig(configWithInvalidHook, undefined, sentryOptions);
@@ -873,9 +867,7 @@ describe('withSentryConfig', () => {
873867
delete configWithoutCompiler.compiler;
874868

875869
const sentryOptions = {
876-
_experimental: {
877-
useRunAfterProductionCompileHook: true,
878-
},
870+
useRunAfterProductionCompileHook: true,
879871
};
880872

881873
const finalConfig = materializeFinalNextConfig(configWithoutCompiler, undefined, sentryOptions);
@@ -890,126 +882,35 @@ describe('withSentryConfig', () => {
890882
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
891883

892884
const sentryOptions = {
893-
_experimental: {
894-
useRunAfterProductionCompileHook: true,
895-
},
896-
};
897-
898-
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
899-
900-
expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function);
901-
902-
delete process.env.TURBOPACK;
903-
});
904-
905-
it('works with webpack builds when TURBOPACK env is not set', () => {
906-
delete process.env.TURBOPACK;
907-
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
908-
909-
const sentryOptions = {
910-
_experimental: {
911-
useRunAfterProductionCompileHook: true,
912-
},
913-
};
914-
915-
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
916-
917-
expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function);
918-
});
919-
});
920-
921-
describe('experimental flag handling', () => {
922-
afterEach(() => {
923-
vi.restoreAllMocks();
924-
});
925-
926-
it('respects useRunAfterProductionCompileHook: true', () => {
927-
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
928-
929-
const sentryOptions = {
930-
_experimental: {
931-
useRunAfterProductionCompileHook: true,
932-
},
885+
useRunAfterProductionCompileHook: true,
933886
};
934887

888+
// Use a clean copy of the config to avoid test interference
935889
const cleanConfig = { ...exportedNextConfig };
936890
delete cleanConfig.compiler;
937891

938892
const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions);
939893

940894
expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function);
941-
});
942895

943-
it('respects useRunAfterProductionCompileHook: false', () => {
944-
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
945-
946-
const sentryOptions = {
947-
_experimental: {
948-
useRunAfterProductionCompileHook: false,
949-
},
950-
};
951-
952-
const cleanConfig = { ...exportedNextConfig };
953-
delete cleanConfig.compiler;
954-
955-
const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions);
956-
957-
expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined();
958-
});
959-
960-
it('does not set up hook when experimental flag is undefined', () => {
961-
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
962-
963-
const sentryOptions = {
964-
_experimental: {
965-
// useRunAfterProductionCompileHook not specified
966-
},
967-
};
968-
969-
const cleanConfig = { ...exportedNextConfig };
970-
delete cleanConfig.compiler;
971-
972-
const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions);
973-
974-
expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined();
896+
delete process.env.TURBOPACK;
975897
});
976898

977-
it('does not set up hook when _experimental is undefined', () => {
899+
it('works with webpack builds when TURBOPACK env is not set', () => {
900+
delete process.env.TURBOPACK;
978901
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
979902

980903
const sentryOptions = {
981-
// no _experimental property
904+
useRunAfterProductionCompileHook: true,
982905
};
983906

907+
// Use a clean copy of the config to avoid test interference
984908
const cleanConfig = { ...exportedNextConfig };
985909
delete cleanConfig.compiler;
986910

987911
const finalConfig = materializeFinalNextConfig(cleanConfig, undefined, sentryOptions);
988912

989-
expect(finalConfig.compiler?.runAfterProductionCompile).toBeUndefined();
990-
});
991-
992-
it('combines experimental flag with other configurations correctly', () => {
993-
process.env.TURBOPACK = '1';
994-
vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.4.1');
995-
vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true);
996-
997-
const sentryOptions = {
998-
_experimental: {
999-
useRunAfterProductionCompileHook: true,
1000-
},
1001-
sourcemaps: {},
1002-
tunnelRoute: '/tunnel',
1003-
};
1004-
1005-
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
1006-
1007-
// Should have both turbopack sourcemap config AND runAfterProductionCompile hook
1008-
expect(finalConfig.productionBrowserSourceMaps).toBe(true);
1009913
expect(finalConfig.compiler?.runAfterProductionCompile).toBeInstanceOf(Function);
1010-
expect(finalConfig.rewrites).toBeInstanceOf(Function);
1011-
1012-
delete process.env.TURBOPACK;
1013914
});
1014915
});
1015916
});

0 commit comments

Comments
 (0)