diff --git a/docs/migration/v8-to-v9.md b/docs/migration/v8-to-v9.md index 055c678e0d06..390d455c891b 100644 --- a/docs/migration/v8-to-v9.md +++ b/docs/migration/v8-to-v9.md @@ -100,6 +100,10 @@ In v9, an `undefined` value will be treated the same as if the value is not defi This behavior was changed because the Next.js Build ID is non-deterministic and the release name is injected into client bundles, causing build artifacts to be non-deterministic. This caused issues for some users. Additionally, because it is uncertain whether it will be possible to rely on a Build ID when Turbopack becomes stable, we decided to pull the plug now instead of introducing confusing behavior in the future. +- Source maps are now automatically enabled for both client and server builds unless explicitly disabled via `sourcemaps.disable`. Client builds use `hidden-source-map` while server builds use `source-map` as their webpack `devtool` setting unless any other value than `false` or `undefined` has been assigned already. + +- By default, source maps will now be automatically deleted after being uploaded to Sentry for client-side builds. You can opt out of this behavior by explicitly setting `sourcemaps.deleteSourcemapsAfterUpload` to `false` in your Sentry config. + ### All Meta-Framework SDKs (`@sentry/astro`, `@sentry/nuxt`) - Updated source map generation to respect the user-provided value of your build config, such as `vite.build.sourcemap`: diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 9a218bda6435..80b46570c03e 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -336,23 +336,36 @@ export function constructWebpackConfigFunction( if (sentryWebpackPlugin) { if (!userSentryOptions.sourcemaps?.disable) { - // TODO(v9): Remove this warning and print warning in case source map deletion is auto configured - if (!isServer && !userSentryOptions.sourcemaps?.deleteSourcemapsAfterUpload) { - // eslint-disable-next-line no-console - console.warn( - "[@sentry/nextjs] The Sentry SDK has enabled source map generation for your Next.js app. If you don't want to serve Source Maps to your users, either set the `sourcemaps.deleteSourcemapsAfterUpload` option to true, or manually delete the source maps after the build. In future Sentry SDK versions `sourcemaps.deleteSourcemapsAfterUpload` will default to `true`. If you do not want to generate and upload sourcemaps, set the `sourcemaps.disable` option in `withSentryConfig()`.", - ); + // Source maps can be configured in 3 ways: + // 1. (next config): productionBrowserSourceMaps + // 2. (next config): experimental.serverSourceMaps + // 3. custom webpack configuration + // + // We only update this if no explicit value is set + // (Next.js defaults to `false`: https://github.com/vercel/next.js/blob/5f4f96c133bd6b10954812cc2fef6af085b82aa5/packages/next/src/build/webpack/config/blocks/base.ts#L61) + if (!newConfig.devtool) { + logger.info(`[@sentry/nextjs] Automatically enabling source map generation for ${runtime} build.`); + // `hidden-source-map` produces the same sourcemaps as `source-map`, but doesn't include the `sourceMappingURL` + // comment at the bottom. For folks who aren't publicly hosting their sourcemaps, this is helpful because then + // the browser won't look for them and throw errors into the console when it can't find them. Because this is a + // front-end-only problem, and because `sentry-cli` handles sourcemaps more reliably with the comment than + // without, the option to use `hidden-source-map` only applies to the client-side build. + if (isServer) { + newConfig.devtool = 'source-map'; + } else { + newConfig.devtool = 'hidden-source-map'; + } } - // `hidden-source-map` produces the same sourcemaps as `source-map`, but doesn't include the `sourceMappingURL` - // comment at the bottom. For folks who aren't publicly hosting their sourcemaps, this is helpful because then - // the browser won't look for them and throw errors into the console when it can't find them. Because this is a - // front-end-only problem, and because `sentry-cli` handles sourcemaps more reliably with the comment than - // without, the option to use `hidden-source-map` only applies to the client-side build. - if (isServer || userNextConfig.productionBrowserSourceMaps) { - newConfig.devtool = 'source-map'; - } else { - newConfig.devtool = 'hidden-source-map'; + // enable source map deletion if not explicitly disabled + if (!isServer && userSentryOptions.sourcemaps?.deleteSourcemapsAfterUpload === undefined) { + logger.warn( + '[@sentry/nextjs] Source maps will be automatically deleted after being uploaded to Sentry. If you want to keep the source maps, set the `sourcemaps.deleteSourcemapsAfterUpload` option to false in `withSentryConfig()`. If you do not want to generate and upload sourcemaps at all, set the `sourcemaps.disable` option to true.', + ); + userSentryOptions.sourcemaps = { + ...userSentryOptions.sourcemaps, + deleteSourcemapsAfterUpload: true, + }; } } diff --git a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts index 20af1d99f1ce..4e99dd61950b 100644 --- a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts +++ b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts @@ -1,6 +1,7 @@ // mock helper functions not tested directly in this file import '../mocks'; +import * as getWebpackPluginOptionsModule from '../../../src/config/webpackPluginOptions'; import { CLIENT_SDK_CONFIG_FILE, clientBuildContext, @@ -29,6 +30,48 @@ describe('constructWebpackConfigFunction()', () => { ); }); + it('preserves existing devtool setting', async () => { + const customDevtool = 'eval-source-map'; + const finalWebpackConfig = await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: { + ...serverWebpackConfig, + devtool: customDevtool, + }, + incomingWebpackBuildContext: serverBuildContext, + sentryBuildTimeOptions: {}, + }); + + expect(finalWebpackConfig.devtool).toEqual(customDevtool); + }); + + it('automatically enables deleteSourcemapsAfterUpload for client builds when not explicitly set', async () => { + const getWebpackPluginOptionsSpy = jest.spyOn(getWebpackPluginOptionsModule, 'getWebpackPluginOptions'); + + await materializeFinalWebpackConfig({ + exportedNextConfig, + incomingWebpackConfig: clientWebpackConfig, + incomingWebpackBuildContext: clientBuildContext, + sentryBuildTimeOptions: { + sourcemaps: {}, + }, + }); + + expect(getWebpackPluginOptionsSpy).toHaveBeenCalledWith( + expect.objectContaining({ + isServer: false, + }), + expect.objectContaining({ + sourcemaps: { + deleteSourcemapsAfterUpload: true, + }, + }), + undefined, + ); + + getWebpackPluginOptionsSpy.mockRestore(); + }); + it('preserves unrelated webpack config options', async () => { const finalWebpackConfig = await materializeFinalWebpackConfig({ exportedNextConfig,