diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 468ec1e1057b..c2164fc37526 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -9,6 +9,10 @@ export type ExportedNextConfig = NextConfigObject | NextConfigFunction; export type NextConfigObject = { // custom webpack options webpack?: WebpackConfigFunction; + sentry?: { + disableServerWebpackPlugin?: boolean; + disableClientWebpackPlugin?: boolean; + }; } & { // other `next.config.js` options [key: string]: unknown; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 9ef791b2e1a1..5b4e0dd58012 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -69,16 +69,6 @@ export function constructWebpackConfigFunction( newConfig = userNextConfig.webpack(newConfig, options); } - // Ensure quality source maps in production. (Source maps aren't uploaded in dev, and besides, Next doesn't let you - // change this is dev even if you want to - see - // https://github.com/vercel/next.js/blob/master/errors/improper-devtool.md.) - if (!options.dev) { - // TODO Handle possibility that user is using `SourceMapDevToolPlugin` (see - // https://webpack.js.org/plugins/source-map-dev-tool-plugin/) - // TODO Give user option to use `hidden-source-map` ? - newConfig.devtool = 'source-map'; - } - // Tell webpack to inject user config files (containing the two `Sentry.init()` calls) into the appropriate output // bundles. Store a separate reference to the original `entry` value to avoid an infinite loop. (If we don't do // this, we'll have a statement of the form `x.y = () => f(x.y)`, where one of the things `f` does is call `x.y`. @@ -90,19 +80,36 @@ export function constructWebpackConfigFunction( const origEntryProperty = newConfig.entry; newConfig.entry = async () => addSentryToEntryProperty(origEntryProperty, options.isServer); - // Add the Sentry plugin, which uploads source maps to Sentry when not in dev - checkWebpackPluginOverrides(userSentryWebpackPluginOptions); - newConfig.plugins = newConfig.plugins || []; - newConfig.plugins.push( - // @ts-ignore Our types for the plugin are messed up somehow - TS wants this to be `SentryWebpackPlugin.default`, - // but that's not actually a thing - new SentryWebpackPlugin({ - dryRun: options.dev, - release: getSentryRelease(options.buildId), - ...defaultSentryWebpackPluginOptions, - ...userSentryWebpackPluginOptions, - }), - ); + // Enable the Sentry plugin (which uploads source maps to Sentry when not in dev) by default + const enableWebpackPlugin = options.isServer + ? !userNextConfig.sentry?.disableServerWebpackPlugin + : !userNextConfig.sentry?.disableClientWebpackPlugin; + + if (enableWebpackPlugin) { + // TODO Handle possibility that user is using `SourceMapDevToolPlugin` (see + // https://webpack.js.org/plugins/source-map-dev-tool-plugin/) + // TODO Give user option to use `hidden-source-map` ? + + // Next doesn't let you change this is dev even if you want to - see + // https://github.com/vercel/next.js/blob/master/errors/improper-devtool.md + if (!options.dev) { + newConfig.devtool = 'source-map'; + } + + checkWebpackPluginOverrides(userSentryWebpackPluginOptions); + + newConfig.plugins = newConfig.plugins || []; + newConfig.plugins.push( + // @ts-ignore Our types for the plugin are messed up somehow - TS wants this to be `SentryWebpackPlugin.default`, + // but that's not actually a thing + new SentryWebpackPlugin({ + dryRun: options.dev, + release: getSentryRelease(options.buildId), + ...defaultSentryWebpackPluginOptions, + ...userSentryWebpackPluginOptions, + }), + ); + } return newConfig; }; diff --git a/packages/nextjs/test/config.test.ts b/packages/nextjs/test/config.test.ts index 09abc3d94c13..3938704efaaf 100644 --- a/packages/nextjs/test/config.test.ts +++ b/packages/nextjs/test/config.test.ts @@ -275,4 +275,43 @@ describe('Sentry webpack plugin config', () => { it("merges default include and ignore/ignoreFile options with user's values", () => { // do we even want to do this? }); + + it('allows SentryWebpackPlugin to be turned off for client code (independent of server code)', () => { + const clientFinalNextConfig = materializeFinalNextConfig({ + ...userNextConfig, + sentry: { disableClientWebpackPlugin: true }, + }); + const clientFinalWebpackConfig = clientFinalNextConfig.webpack?.(clientWebpackConfig, clientBuildContext); + + const serverFinalNextConfig = materializeFinalNextConfig(userNextConfig, userSentryWebpackPluginConfig); + const serverFinalWebpackConfig = serverFinalNextConfig.webpack?.(serverWebpackConfig, serverBuildContext); + + expect(clientFinalWebpackConfig?.plugins).not.toEqual(expect.arrayContaining([expect.any(SentryWebpackPlugin)])); + expect(serverFinalWebpackConfig?.plugins).toEqual(expect.arrayContaining([expect.any(SentryWebpackPlugin)])); + }); + + it('allows SentryWebpackPlugin to be turned off for server code (independent of client code)', () => { + const serverFinalNextConfig = materializeFinalNextConfig({ + ...userNextConfig, + sentry: { disableServerWebpackPlugin: true }, + }); + const serverFinalWebpackConfig = serverFinalNextConfig.webpack?.(serverWebpackConfig, serverBuildContext); + + const clientFinalNextConfig = materializeFinalNextConfig(userNextConfig, userSentryWebpackPluginConfig); + const clientFinalWebpackConfig = clientFinalNextConfig.webpack?.(clientWebpackConfig, clientBuildContext); + + expect(serverFinalWebpackConfig?.plugins).not.toEqual(expect.arrayContaining([expect.any(SentryWebpackPlugin)])); + expect(clientFinalWebpackConfig?.plugins).toEqual(expect.arrayContaining([expect.any(SentryWebpackPlugin)])); + }); + + it("doesn't set devtool if webpack plugin is disabled", () => { + const finalNextConfig = materializeFinalNextConfig({ + ...userNextConfig, + webpack: () => ({ devtool: 'something-besides-source-map' } as any), + sentry: { disableServerWebpackPlugin: true }, + }); + const finalWebpackConfig = finalNextConfig.webpack?.(serverWebpackConfig, serverBuildContext); + + expect(finalWebpackConfig?.devtool).not.toEqual('source-map'); + }); });