Skip to content

Commit eb4382c

Browse files
authored
feat(nextjs): Add sourcemaps.filesToDeleteAfterUpload as a top-level option (#19280)
- Adds `sourcemaps.filesToDeleteAfterUpload` to the Next.js SDK's SentryBuildOptions type, allowing users to specify custom glob patterns for source map deletion after upload. - When set, this option overrides the default deletion patterns computed by `deleteSourcemapsAfterUpload`, giving users fine-grained control — including the ability to target server-side source maps if desired. closes #19235
1 parent 6573b00 commit eb4382c

File tree

3 files changed

+116
-6
lines changed

3 files changed

+116
-6
lines changed

packages/nextjs/src/config/getBuildPluginOptions.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,27 @@ export function getBuildPluginOptions({
284284

285285
const finalIgnorePatterns = mergeIgnorePatterns(sourcemapUploadIgnore, sentryBuildOptions.sourcemaps?.ignore);
286286

287-
const filesToDeleteAfterUpload = createFilesToDeleteAfterUploadPattern(
288-
normalizedDistDirAbsPath,
289-
buildTool,
290-
deleteSourcemapsAfterUpload,
291-
useRunAfterProductionCompileHook,
292-
);
287+
const userFilesToDeleteAfterUpload = sentryBuildOptions.sourcemaps?.filesToDeleteAfterUpload;
288+
289+
if (sentryBuildOptions.debug && userFilesToDeleteAfterUpload !== undefined) {
290+
// eslint-disable-next-line no-console
291+
console.debug(
292+
'[@sentry/nextjs] Skipping auto-deletion of source maps as user has provided filesToDeleteAfterUpload:',
293+
userFilesToDeleteAfterUpload,
294+
);
295+
}
296+
297+
const filesToDeleteAfterUpload =
298+
userFilesToDeleteAfterUpload !== undefined
299+
? Array.isArray(userFilesToDeleteAfterUpload)
300+
? userFilesToDeleteAfterUpload
301+
: [userFilesToDeleteAfterUpload]
302+
: createFilesToDeleteAfterUploadPattern(
303+
normalizedDistDirAbsPath,
304+
buildTool,
305+
deleteSourcemapsAfterUpload,
306+
useRunAfterProductionCompileHook,
307+
);
293308

294309
const skipSourcemapsUpload = shouldSkipSourcemapUpload(buildTool, useRunAfterProductionCompileHook);
295310

packages/nextjs/src/config/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,24 @@ export type SentryBuildOptions = {
276276
* Defaults to `true`.
277277
*/
278278
deleteSourcemapsAfterUpload?: boolean;
279+
280+
/**
281+
* A glob or an array of globs that specifies which source map files should be deleted after being uploaded to Sentry.
282+
*
283+
* When set, this overrides the default deletion behavior of `deleteSourcemapsAfterUpload`.
284+
*
285+
* Use this option when you need fine-grained control over which source maps are deleted.
286+
*
287+
* @example
288+
* ```javascript
289+
* withSentryConfig(nextConfig, {
290+
* sourcemaps: {
291+
* filesToDeleteAfterUpload: ['.next/static/**\/*.map'],
292+
* },
293+
* });
294+
* ```
295+
*/
296+
filesToDeleteAfterUpload?: string | string[];
279297
};
280298

281299
/**

packages/nextjs/test/config/getBuildPluginOptions.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,83 @@ describe('getBuildPluginOptions', () => {
473473
expect(result.sourcemaps?.filesToDeleteAfterUpload).toBeUndefined();
474474
});
475475

476+
it('uses custom filesToDeleteAfterUpload string when provided', () => {
477+
const sentryBuildOptions: SentryBuildOptions = {
478+
org: 'test-org',
479+
project: 'test-project',
480+
sourcemaps: {
481+
filesToDeleteAfterUpload: '.next/static/**/*.map',
482+
},
483+
};
484+
485+
const result = getBuildPluginOptions({
486+
sentryBuildOptions,
487+
releaseName: mockReleaseName,
488+
distDirAbsPath: mockDistDirAbsPath,
489+
buildTool: 'webpack-client',
490+
});
491+
492+
expect(result.sourcemaps?.filesToDeleteAfterUpload).toEqual(['.next/static/**/*.map']);
493+
});
494+
495+
it('uses custom filesToDeleteAfterUpload array when provided', () => {
496+
const sentryBuildOptions: SentryBuildOptions = {
497+
org: 'test-org',
498+
project: 'test-project',
499+
sourcemaps: {
500+
filesToDeleteAfterUpload: ['.next/static/**/*.map', '.next/server/**/*.map'],
501+
},
502+
};
503+
504+
const result = getBuildPluginOptions({
505+
sentryBuildOptions,
506+
releaseName: mockReleaseName,
507+
distDirAbsPath: mockDistDirAbsPath,
508+
buildTool: 'webpack-client',
509+
});
510+
511+
expect(result.sourcemaps?.filesToDeleteAfterUpload).toEqual(['.next/static/**/*.map', '.next/server/**/*.map']);
512+
});
513+
514+
it('filesToDeleteAfterUpload overrides deleteSourcemapsAfterUpload default pattern', () => {
515+
const sentryBuildOptions: SentryBuildOptions = {
516+
org: 'test-org',
517+
project: 'test-project',
518+
sourcemaps: {
519+
deleteSourcemapsAfterUpload: true,
520+
filesToDeleteAfterUpload: ['custom/**/*.map'],
521+
},
522+
};
523+
524+
const result = getBuildPluginOptions({
525+
sentryBuildOptions,
526+
releaseName: mockReleaseName,
527+
distDirAbsPath: mockDistDirAbsPath,
528+
buildTool: 'webpack-client',
529+
});
530+
531+
expect(result.sourcemaps?.filesToDeleteAfterUpload).toEqual(['custom/**/*.map']);
532+
});
533+
534+
it('filesToDeleteAfterUpload bypasses server build guard', () => {
535+
const sentryBuildOptions: SentryBuildOptions = {
536+
org: 'test-org',
537+
project: 'test-project',
538+
sourcemaps: {
539+
filesToDeleteAfterUpload: ['.next/server/**/*.map'],
540+
},
541+
};
542+
543+
const result = getBuildPluginOptions({
544+
sentryBuildOptions,
545+
releaseName: mockReleaseName,
546+
distDirAbsPath: mockDistDirAbsPath,
547+
buildTool: 'webpack-nodejs',
548+
});
549+
550+
expect(result.sourcemaps?.filesToDeleteAfterUpload).toEqual(['.next/server/**/*.map']);
551+
});
552+
476553
it('uses custom sourcemap assets when provided', () => {
477554
const customAssets = ['custom/path/**', 'another/path/**'];
478555
const sentryBuildOptions: SentryBuildOptions = {

0 commit comments

Comments
 (0)