diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index fd8602e29741..6ae7a52aae11 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -54,10 +54,13 @@ export type WebpackEntryProperty = EntryPropertyObject | EntryPropertyFunction; // Each value in that object is either a string representing a single entry point, an array of such strings, or an // object containing either of those, along with other configuration options. In that third case, the entry point(s) are // listed under the key `import`. -export type EntryPropertyObject = - | { [key: string]: string } - | { [key: string]: Array } - | { [key: string]: EntryPointObject }; // only in webpack 5 +export type EntryPropertyObject = { + [key: string]: + | string + | Array + // only in webpack 5 + | EntryPointObject; +}; export type EntryPropertyFunction = () => Promise; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 5d5edaa4e62a..55ac3eb68d7f 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -4,6 +4,7 @@ import * as SentryWebpackPlugin from '@sentry/webpack-plugin'; import { BuildContext, + EntryPointObject, EntryPropertyObject, ExportedNextConfig, SentryWebpackPluginOptions, @@ -151,6 +152,23 @@ async function addSentryToEntryProperty( // On the client, it's sufficient to inject it into the `main` JS code, which is included in every browser page. else { addFileToExistingEntryPoint(newEntryProperty, 'main', SENTRY_CLIENT_CONFIG_FILE); + + // To work around a bug in nextjs, we need to ensure that the `main.js` entry is empty (otherwise it'll choose that + // over `main` and we'll lose the change we just made). In case some other library has put something into it, copy + // its contents over before emptying it out. See + // https://github.com/getsentry/sentry-javascript/pull/3696#issuecomment-863363803.) + const mainjsValue = newEntryProperty['main.js']; + if (Array.isArray(mainjsValue) && mainjsValue.length > 0) { + const mainValue = newEntryProperty.main; + + // copy the `main.js` entries over + newEntryProperty.main = Array.isArray(mainValue) + ? [...mainjsValue, ...mainValue] + : { ...(mainValue as EntryPointObject), import: [...mainjsValue, ...(mainValue as EntryPointObject).import] }; + + // nuke the entries + newEntryProperty['main.js'] = []; + } } return newEntryProperty; diff --git a/packages/nextjs/test/config.test.ts b/packages/nextjs/test/config.test.ts index 5265abbf71aa..4abe6c8426ce 100644 --- a/packages/nextjs/test/config.test.ts +++ b/packages/nextjs/test/config.test.ts @@ -212,7 +212,27 @@ describe('webpack config', () => { }); expect(finalWebpackConfig.entry).toEqual( - expect.objectContaining({ main: expect.arrayContaining(['./sentry.client.config.js']) }), + expect.objectContaining({ main: ['./src/index.ts', './sentry.client.config.js'] }), + ); + }); + + // see https://github.com/getsentry/sentry-javascript/pull/3696#issuecomment-863363803 + it('handles non-empty `main.js` entry point', async () => { + const finalWebpackConfig = await materializeFinalWebpackConfig({ + userNextConfig, + userSentryWebpackPluginConfig, + incomingWebpackConfig: { + ...clientWebpackConfig, + entry: () => Promise.resolve({ main: './src/index.ts', 'main.js': ['sitLieDownRollOver.config.js'] }), + }, + incomingWebpackBuildContext: { ...buildContext, isServer: false }, + }); + + expect(finalWebpackConfig.entry).toEqual( + expect.objectContaining({ + main: ['sitLieDownRollOver.config.js', './src/index.ts', './sentry.client.config.js'], + 'main.js': [], + }), ); }); });