From 46d45ccc50c69875d2294729403750d245cfff53 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Tue, 20 Oct 2026 11:47:29 -0400 Subject: [PATCH] fix(v10/nextjs): Only inject applicationKey metadata into browser bundles Backport of: #25565 --- .../src/config/getBuildPluginOptions.ts | 3 +++ .../turbopack/constructTurbopackConfig.ts | 3 ++- .../test/config/getBuildPluginOptions.test.ts | 25 +++++++++++++++++++ .../constructTurbopackConfig.test.ts | 10 ++++---- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/src/config/getBuildPluginOptions.ts b/packages/nextjs/src/config/getBuildPluginOptions.ts index 4426858822fa..247c43dfcfba 100644 --- a/packages/nextjs/src/config/getBuildPluginOptions.ts +++ b/packages/nextjs/src/config/getBuildPluginOptions.ts @@ -357,5 +357,8 @@ export function getBuildPluginOptions({ }, }, ...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions, + // Only the browser reads these tags (`thirdPartyErrorFilterIntegration`), so server bundles skip the per-module cost. + // Set after the spread so a key passed through `unstable_sentryWebpackPluginOptions` is dropped on the server too. + ...(buildTool === 'webpack-nodejs' || buildTool === 'webpack-edge' ? { applicationKey: undefined } : {}), }; } diff --git a/packages/nextjs/src/config/turbopack/constructTurbopackConfig.ts b/packages/nextjs/src/config/turbopack/constructTurbopackConfig.ts index 20c549db3bbb..919a713a5353 100644 --- a/packages/nextjs/src/config/turbopack/constructTurbopackConfig.ts +++ b/packages/nextjs/src/config/turbopack/constructTurbopackConfig.ts @@ -67,13 +67,14 @@ export function constructTurbopackConfig({ // so it is safe even for node_modules with strict initialization order. // We only exclude Next.js build polyfills which contain non-standard syntax that causes // parse errors when any code is prepended (Turbopack re-parses the loader output). + // Server and edge modules are skipped because only the browser reads these tags. // eslint-disable-next-line typescript/no-deprecated const applicationKey = userSentryOptions?.applicationKey ?? userSentryOptions?._experimental?.turbopackApplicationKey; if (applicationKey && nextJsVersion && supportsTurbopackRuleCondition(nextJsVersion)) { newConfig.rules = safelyAddTurbopackRule(newConfig.rules, { matcher: '*.{ts,tsx,js,jsx,mjs,cjs}', rule: { - condition: { not: { path: /next\/dist\/build\/polyfills/ } }, + condition: { all: ['browser', { not: { path: /next\/dist\/build\/polyfills/ } }] }, loaders: [ { loader: path.resolve(__dirname, '..', 'loaders', 'moduleMetadataInjectionLoader.js'), diff --git a/packages/nextjs/test/config/getBuildPluginOptions.test.ts b/packages/nextjs/test/config/getBuildPluginOptions.test.ts index 4b29c4a6c977..4eb74a00c450 100644 --- a/packages/nextjs/test/config/getBuildPluginOptions.test.ts +++ b/packages/nextjs/test/config/getBuildPluginOptions.test.ts @@ -90,6 +90,31 @@ describe('getBuildPluginOptions', () => { expect(result.applicationKey).toBe('my-app-key'); }); + it.each(['webpack-nodejs', 'webpack-edge'] as const)('does not forward applicationKey for %s builds', buildTool => { + const result = getBuildPluginOptions({ + sentryBuildOptions: { applicationKey: 'my-app-key' }, + releaseName: mockReleaseName, + distDirAbsPath: mockDistDirAbsPath, + buildTool, + }); + + expect(result.applicationKey).toBeUndefined(); + }); + + it.each(['webpack-nodejs', 'webpack-edge'] as const)( + 'does not forward applicationKey from unstable_sentryWebpackPluginOptions for %s builds', + buildTool => { + const result = getBuildPluginOptions({ + sentryBuildOptions: { webpack: { unstable_sentryWebpackPluginOptions: { applicationKey: 'my-app-key' } } }, + releaseName: mockReleaseName, + distDirAbsPath: mockDistDirAbsPath, + buildTool, + }); + + expect(result.applicationKey).toBeUndefined(); + }, + ); + it('normalizes Windows paths to posix for glob patterns in after-production-compile builds', () => { const windowsPath = 'C:\\Users\\test\\.next'; const sentryBuildOptions: SentryBuildOptions = { diff --git a/packages/nextjs/test/config/turbopack/constructTurbopackConfig.test.ts b/packages/nextjs/test/config/turbopack/constructTurbopackConfig.test.ts index 25bc45d3ac4c..d41ae0a9ab0e 100644 --- a/packages/nextjs/test/config/turbopack/constructTurbopackConfig.test.ts +++ b/packages/nextjs/test/config/turbopack/constructTurbopackConfig.test.ts @@ -965,7 +965,7 @@ describe('moduleMetadataInjection with applicationKey', () => { }); expect(result.rules!['*.{ts,tsx,js,jsx,mjs,cjs}']).toEqual({ - condition: { not: { path: /next\/dist\/build\/polyfills/ } }, + condition: { all: ['browser', { not: { path: /next\/dist\/build\/polyfills/ } }] }, loaders: [ { loader: '/mocked/path/to/moduleMetadataInjectionLoader.js', @@ -1010,7 +1010,7 @@ describe('moduleMetadataInjection with applicationKey', () => { expect(rule.loaders[0]!.options.applicationKey).toBe('top-level-key'); }); - it('should only exclude Next.js polyfills, not all foreign modules', () => { + it('should only tag browser modules and exclude Next.js polyfills, not all foreign modules', () => { const userNextConfig: NextConfigObject = {}; const result = constructTurbopackConfig({ @@ -1019,11 +1019,11 @@ describe('moduleMetadataInjection with applicationKey', () => { nextJsVersion: '16.0.0', }); - const rule = result.rules!['*.{ts,tsx,js,jsx,mjs,cjs}'] as { condition?: { not: unknown }; loaders: unknown[] }; + const rule = result.rules!['*.{ts,tsx,js,jsx,mjs,cjs}'] as { condition?: unknown; loaders: unknown[] }; // Unlike component annotation (which uses { not: 'foreign' }), metadata injection // must cover node_modules to match the webpack plugin's BannerPlugin behavior. // Only Next.js build polyfills are excluded because they have non-standard syntax. - expect(rule.condition).toEqual({ not: { path: /next\/dist\/build\/polyfills/ } }); + expect(rule.condition).toEqual({ all: ['browser', { not: { path: /next\/dist\/build\/polyfills/ } }] }); }); it('should NOT add metadata loader rule when Next.js < 16', () => { @@ -1118,7 +1118,7 @@ describe('moduleMetadataInjection with applicationKey', () => { }); expect(result.rules!['*.{ts,tsx,js,jsx,mjs,cjs}']).toEqual({ - condition: { not: { path: /next\/dist\/build\/polyfills/ } }, + condition: { all: ['browser', { not: { path: /next\/dist\/build\/polyfills/ } }] }, loaders: [ { loader: '/mocked/path/to/moduleMetadataInjectionLoader.js',