diff --git a/packages/nextjs/src/config/getBuildPluginOptions.ts b/packages/nextjs/src/config/getBuildPluginOptions.ts index e43061eb59a5..dbc84e88be40 100644 --- a/packages/nextjs/src/config/getBuildPluginOptions.ts +++ b/packages/nextjs/src/config/getBuildPluginOptions.ts @@ -37,6 +37,13 @@ const FILE_PATTERNS = { FRAMEWORK_CHUNKS_DOT: 'static/chunks/framework.*', POLYFILLS_CHUNKS: 'static/chunks/polyfills-*', WEBPACK_CHUNKS: 'static/chunks/webpack-*', + PAGE_CLIENT_REFERENCE_MANIFEST: '**/page_client-reference-manifest.js', + SERVER_REFERENCE_MANIFEST: '**/server-reference-manifest.js', + NEXT_FONT_MANIFEST: '**/next-font-manifest.js', + MIDDLEWARE_BUILD_MANIFEST: '**/middleware-build-manifest.js', + INTERCEPTION_ROUTE_REWRITE_MANIFEST: '**/interception-route-rewrite-manifest.js', + ROUTE_CLIENT_REFERENCE_MANIFEST: '**/route_client-reference-manifest.js', + MIDDLEWARE_REACT_LOADABLE_MANIFEST: '**/middleware-react-loadable-manifest.js', } as const; // Source map file extensions to delete @@ -142,6 +149,16 @@ function createSourcemapUploadIgnorePattern( path.posix.join(normalizedDistPath, FILE_PATTERNS.FRAMEWORK_CHUNKS_DOT), path.posix.join(normalizedDistPath, FILE_PATTERNS.POLYFILLS_CHUNKS), path.posix.join(normalizedDistPath, FILE_PATTERNS.WEBPACK_CHUNKS), + // Next.js internal manifest files that don't have source maps + // These files are auto-generated by Next.js and do not contain user code. + // Ignoring them prevents "Could not determine source map reference" warnings. + FILE_PATTERNS.PAGE_CLIENT_REFERENCE_MANIFEST, + FILE_PATTERNS.SERVER_REFERENCE_MANIFEST, + FILE_PATTERNS.NEXT_FONT_MANIFEST, + FILE_PATTERNS.MIDDLEWARE_BUILD_MANIFEST, + FILE_PATTERNS.INTERCEPTION_ROUTE_REWRITE_MANIFEST, + FILE_PATTERNS.ROUTE_CLIENT_REFERENCE_MANIFEST, + FILE_PATTERNS.MIDDLEWARE_REACT_LOADABLE_MANIFEST, ); return ignore; @@ -216,6 +233,20 @@ function createReleaseConfig( }; } +/** + * Merges default ignore patterns with user-provided ignore patterns. + * User patterns are appended to the defaults to ensure internal Next.js + * files are always ignored while allowing users to add additional patterns. + */ +function mergeIgnorePatterns(defaultPatterns: string[], userPatterns: string | string[] | undefined): string[] { + if (!userPatterns) { + return defaultPatterns; + } + + const userPatternsArray = Array.isArray(userPatterns) ? userPatterns : [userPatterns]; + return [...defaultPatterns, ...userPatternsArray]; +} + /** * Get Sentry Build Plugin options for both webpack and turbopack builds. * These options can be used in two ways: @@ -239,7 +270,6 @@ export function getBuildPluginOptions({ // glob characters. This clashes with Windows path separators. // See: https://www.npmjs.com/package/glob const normalizedDistDirAbsPath = normalizePathForGlob(distDirAbsPath); - const loggerPrefix = LOGGER_PREFIXES[buildTool]; const widenClientFileUpload = sentryBuildOptions.widenClientFileUpload ?? false; const deleteSourcemapsAfterUpload = sentryBuildOptions.sourcemaps?.deleteSourcemapsAfterUpload ?? false; @@ -252,6 +282,8 @@ export function getBuildPluginOptions({ const sourcemapUploadIgnore = createSourcemapUploadIgnorePattern(normalizedDistDirAbsPath, widenClientFileUpload); + const finalIgnorePatterns = mergeIgnorePatterns(sourcemapUploadIgnore, sentryBuildOptions.sourcemaps?.ignore); + const filesToDeleteAfterUpload = createFilesToDeleteAfterUploadPattern( normalizedDistDirAbsPath, buildTool, @@ -281,7 +313,7 @@ export function getBuildPluginOptions({ disable: skipSourcemapsUpload ? true : (sentryBuildOptions.sourcemaps?.disable ?? false), rewriteSources: rewriteWebpackSources, assets: sentryBuildOptions.sourcemaps?.assets ?? sourcemapUploadAssets, - ignore: sentryBuildOptions.sourcemaps?.ignore ?? sourcemapUploadIgnore, + ignore: finalIgnorePatterns, filesToDeleteAfterUpload, ...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.sourcemaps, }, diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index cdc6e68f053d..46b1aef110d2 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -261,7 +261,8 @@ export type SentryBuildOptions = { /** * A glob or an array of globs that specifies which build artifacts should not be uploaded to Sentry. * - * Default: `[]` + * The SDK automatically ignores Next.js internal files that don't have source maps (such as manifest files) + * to prevent "Could not determine source map" warnings. Your custom patterns are merged with these defaults. * * The globbing patterns follow the implementation of the `glob` package. (https://www.npmjs.com/package/glob) * diff --git a/packages/nextjs/test/config/getBuildPluginOptions.test.ts b/packages/nextjs/test/config/getBuildPluginOptions.test.ts index 3e95eadafc96..f62463447290 100644 --- a/packages/nextjs/test/config/getBuildPluginOptions.test.ts +++ b/packages/nextjs/test/config/getBuildPluginOptions.test.ts @@ -33,6 +33,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ], filesToDeleteAfterUpload: undefined, rewriteSources: expect.any(Function), @@ -121,6 +128,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ]); expect(result.reactComponentAnnotation).toBeDefined(); }); @@ -142,6 +156,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ]); }); @@ -161,6 +182,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ]); expect(result.reactComponentAnnotation).toBeDefined(); }); @@ -181,6 +209,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ]); expect(result.reactComponentAnnotation).toBeDefined(); }); @@ -205,6 +240,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ]); expect(result.reactComponentAnnotation).toBeUndefined(); }); @@ -228,6 +270,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ]); expect(result.reactComponentAnnotation).toBeUndefined(); }); @@ -444,7 +493,7 @@ describe('getBuildPluginOptions', () => { expect(result.sourcemaps?.assets).toEqual(customAssets); }); - it('uses custom sourcemap ignore patterns when provided', () => { + it('merges custom sourcemap ignore patterns with defaults', () => { const customIgnore = ['**/vendor/**', '**/node_modules/**']; const sentryBuildOptions: SentryBuildOptions = { org: 'test-org', @@ -461,7 +510,58 @@ describe('getBuildPluginOptions', () => { buildTool: 'webpack-client', }); - expect(result.sourcemaps?.ignore).toEqual(customIgnore); + // Custom patterns should be appended to defaults, not replace them + expect(result.sourcemaps?.ignore).toEqual([ + '/path/to/.next/static/chunks/main-*', + '/path/to/.next/static/chunks/framework-*', + '/path/to/.next/static/chunks/framework.*', + '/path/to/.next/static/chunks/polyfills-*', + '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', + '**/vendor/**', + '**/node_modules/**', + ]); + }); + + it('handles single string custom sourcemap ignore pattern', () => { + const customIgnore = '**/vendor/**'; + const sentryBuildOptions: SentryBuildOptions = { + org: 'test-org', + project: 'test-project', + sourcemaps: { + ignore: customIgnore, + }, + }; + + const result = getBuildPluginOptions({ + sentryBuildOptions, + releaseName: mockReleaseName, + distDirAbsPath: mockDistDirAbsPath, + buildTool: 'webpack-client', + }); + + // Single string pattern should be appended to defaults + expect(result.sourcemaps?.ignore).toEqual([ + '/path/to/.next/static/chunks/main-*', + '/path/to/.next/static/chunks/framework-*', + '/path/to/.next/static/chunks/framework.*', + '/path/to/.next/static/chunks/polyfills-*', + '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', + '**/vendor/**', + ]); }); it('disables sourcemaps when disable flag is set', () => { @@ -769,6 +869,13 @@ describe('getBuildPluginOptions', () => { '/path/to/.next/static/chunks/framework.*', '/path/to/.next/static/chunks/polyfills-*', '/path/to/.next/static/chunks/webpack-*', + '**/page_client-reference-manifest.js', + '**/server-reference-manifest.js', + '**/next-font-manifest.js', + '**/middleware-build-manifest.js', + '**/interception-route-rewrite-manifest.js', + '**/route_client-reference-manifest.js', + '**/middleware-react-loadable-manifest.js', ], filesToDeleteAfterUpload: undefined, rewriteSources: expect.any(Function),