Skip to content

Commit 670d842

Browse files
authored
fix(nextjs): Widen scope for client file upload (#4705)
When nextjs builds an app, compiled versions of each page end up in `.next/static/chunks/pages`. In some circumstances, however, user-generated code ends up outside of `pages/`, and in those cases sourcemaps are broken because the relevant files aren't being uploaded. This fixes that by optionally widening the scope of the upload, while excluding files known to contain only nextjs and webpack code. (Every build will generate other files which should be excluded, but there's no way to tell in advance what they're going to be called.) In order to reduce the number of irrelevant files being uploaded to Sentry, this behavior is off by default, but can be turned on with a new option `widenClientFileUpload`: ``` const { withSentryConfig } = require("@sentry/nextjs"); const moduleExports = { sentry: { widenClientFileUpload: true, }, }; const sentryWebpackPluginOptions = { // ... }; module.exports = withSentryConfig(moduleExports, sentryWebpackPluginOptions); ``` This change is documented in getsentry/sentry-docs#4827. Fixes #3896
1 parent 0e15dae commit 670d842

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

packages/nextjs/src/config/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ export type NextConfigObject = {
2121
disableServerWebpackPlugin?: boolean;
2222
disableClientWebpackPlugin?: boolean;
2323
hideSourceMaps?: boolean;
24+
25+
// Upload files from `<distDir>/static/chunks` rather than `<distDir>/static/chunks/pages`. Usually files outside of
26+
// `pages/` only contain third-party code, but in cases where they contain user code, restricting the webpack
27+
// plugin's upload breaks sourcemaps for those user-code-containing files, because it keeps them from being
28+
// uploaded. At the same time, we don't want to widen the scope if we don't have to, because we're guaranteed to end
29+
// up uploading too many files, which is why this defaults to `false`.
30+
widenClientFileUpload?: boolean;
2431
};
2532
} & {
2633
// other `next.config.js` options

packages/nextjs/src/config/webpack.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,19 @@ export function getWebpackPluginOptions(
294294
isWebpack5 ? [{ paths: [`${distDir}/server/chunks/`], urlPrefix: `${urlPrefix}/server/chunks` }] : [],
295295
);
296296

297-
const clientInclude = [{ paths: [`${distDir}/static/chunks/pages`], urlPrefix: `${urlPrefix}/static/chunks/pages` }];
297+
const clientInclude = userNextConfig.sentry?.widenClientFileUpload
298+
? [{ paths: [`${distDir}/static/chunks`], urlPrefix: `${urlPrefix}/static/chunks` }]
299+
: [{ paths: [`${distDir}/static/chunks/pages`], urlPrefix: `${urlPrefix}/static/chunks/pages` }];
298300

299301
const defaultPluginOptions = dropUndefinedKeys({
300302
include: isServer ? serverInclude : clientInclude,
301-
ignore: [],
303+
ignore:
304+
isServer || !userNextConfig.sentry?.widenClientFileUpload
305+
? []
306+
: // Widening the upload scope is necessarily going to lead to us uploading files we don't need to (ones which
307+
// don't include any user code). In order to lessen that where we can, exclude the internal nextjs files we know
308+
// will be there.
309+
['framework-*', 'framework.*', 'main-*', 'polyfills-*', 'webpack-*'],
302310
url: process.env.SENTRY_URL,
303311
org: process.env.SENTRY_ORG,
304312
project: process.env.SENTRY_PROJECT,

packages/nextjs/test/config.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,24 @@ describe('Sentry webpack plugin config', () => {
598598
]);
599599
});
600600

601+
it('has the correct value when building client bundles using `widenClientFileUpload` option', async () => {
602+
const userNextConfigWithWidening = { ...userNextConfig, sentry: { widenClientFileUpload: true } };
603+
const finalWebpackConfig = await materializeFinalWebpackConfig({
604+
userNextConfig: userNextConfigWithWidening,
605+
incomingWebpackConfig: clientWebpackConfig,
606+
incomingWebpackBuildContext: getBuildContext('client', userNextConfigWithWidening),
607+
});
608+
609+
const sentryWebpackPluginInstance = findWebpackPlugin(
610+
finalWebpackConfig,
611+
'SentryCliPlugin',
612+
) as SentryWebpackPlugin;
613+
614+
expect(sentryWebpackPluginInstance.options.include).toEqual([
615+
{ paths: ['.next/static/chunks'], urlPrefix: '~/_next/static/chunks' },
616+
]);
617+
});
618+
601619
it('has the correct value when building serverless server bundles', async () => {
602620
const userNextConfigServerless = { ...userNextConfig };
603621
userNextConfigServerless.target = 'experimental-serverless-trace';
@@ -657,6 +675,45 @@ describe('Sentry webpack plugin config', () => {
657675
});
658676
});
659677

678+
describe('Sentry webpack plugin `ignore` option', () => {
679+
it('has the correct value when building client bundles', async () => {
680+
const finalWebpackConfig = await materializeFinalWebpackConfig({
681+
userNextConfig,
682+
incomingWebpackConfig: clientWebpackConfig,
683+
incomingWebpackBuildContext: clientBuildContext,
684+
});
685+
686+
const sentryWebpackPluginInstance = findWebpackPlugin(
687+
finalWebpackConfig,
688+
'SentryCliPlugin',
689+
) as SentryWebpackPlugin;
690+
691+
expect(sentryWebpackPluginInstance.options.ignore).toEqual([]);
692+
});
693+
694+
it('has the correct value when building client bundles using `widenClientFileUpload` option', async () => {
695+
const userNextConfigWithWidening = { ...userNextConfig, sentry: { widenClientFileUpload: true } };
696+
const finalWebpackConfig = await materializeFinalWebpackConfig({
697+
userNextConfig: userNextConfigWithWidening,
698+
incomingWebpackConfig: clientWebpackConfig,
699+
incomingWebpackBuildContext: getBuildContext('client', userNextConfigWithWidening),
700+
});
701+
702+
const sentryWebpackPluginInstance = findWebpackPlugin(
703+
finalWebpackConfig,
704+
'SentryCliPlugin',
705+
) as SentryWebpackPlugin;
706+
707+
expect(sentryWebpackPluginInstance.options.ignore).toEqual([
708+
'framework-*',
709+
'framework.*',
710+
'main-*',
711+
'polyfills-*',
712+
'webpack-*',
713+
]);
714+
});
715+
});
716+
660717
describe("Sentry webpack plugin `include` option with basePath filled on next's config", () => {
661718
const userNextConfigWithBasePath = {
662719
...userNextConfig,

0 commit comments

Comments
 (0)