diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 77b7316ed0ca..c9d6b4de9a71 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -18,6 +18,7 @@ export type NextConfigObject = { // the output directory for the built app (defaults to ".next") distDir: string; sentry?: { + userConfigDir?: string; disableServerWebpackPlugin?: boolean; disableClientWebpackPlugin?: boolean; }; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 98bd56b66a57..89fa1500f2b5 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -59,7 +59,7 @@ export function constructWebpackConfigFunction( // will call the callback which will call `f` which will call `x.y`... and on and on. Theoretically this could also // be fixed by using `bind`, but this is way simpler.) const origEntryProperty = newConfig.entry; - newConfig.entry = async () => addSentryToEntryProperty(origEntryProperty, buildContext); + newConfig.entry = async () => addSentryToEntryProperty(origEntryProperty, buildContext, userNextConfig); // In webpack 5, you can get webpack to replace any module you'd like with an empty object, just by setting its // `resolve.alias` value to `false`. Not much of our code is neatly separated into "things node needs" and "things @@ -119,6 +119,7 @@ export function constructWebpackConfigFunction( async function addSentryToEntryProperty( currentEntryProperty: WebpackEntryProperty, buildContext: BuildContext, + userNextConfig: Partial, ): Promise { // The `entry` entry in a webpack config can be a string, array of strings, object, or function. By default, nextjs // sets it to an async function which returns the promise of an object of string arrays. Because we don't know whether @@ -129,10 +130,11 @@ async function addSentryToEntryProperty( const newEntryProperty = typeof currentEntryProperty === 'function' ? await currentEntryProperty() : { ...currentEntryProperty }; + const userConfigDir = userNextConfig.sentry?.userConfigDir || buildContext.dir; // `sentry.server.config.js` or `sentry.client.config.js` (or their TS equivalents) const userConfigFile = buildContext.isServer - ? getUserConfigFile(buildContext.dir, 'server') - : getUserConfigFile(buildContext.dir, 'client'); + ? getUserConfigFile(userConfigDir, 'server') + : getUserConfigFile(userConfigDir, 'client'); // we need to turn the filename into a path so webpack can find it const filesToInject = [`./${userConfigFile}`]; diff --git a/packages/nextjs/test/config.test.ts b/packages/nextjs/test/config.test.ts index 76b9af438c0c..601f38cc0590 100644 --- a/packages/nextjs/test/config.test.ts +++ b/packages/nextjs/test/config.test.ts @@ -35,7 +35,7 @@ const mockExistsSync = (path: fs.PathLike) => { return realExistsSync(path); }; -const exitsSync = jest.spyOn(fs, 'existsSync').mockImplementation(mockExistsSync); +const existsSync = jest.spyOn(fs, 'existsSync').mockImplementation(mockExistsSync); // Make it so that all temporary folders, either created directly by tests or by the code they're testing, will go into // one spot that we know about, which we can then clean up when we're done @@ -264,6 +264,19 @@ describe('withSentryConfig', () => { expect(userNextConfigFunction).toHaveBeenCalledWith(runtimePhase, defaultsObject); }); + + it(`respects custom userConfigDir`, async () => { + const userNextConfigCustomDir = { + ...userNextConfig, + sentry: { userConfigDir: '/dog_park' }, + }; + await materializeFinalWebpackConfig({ + userNextConfig: userNextConfigCustomDir, + incomingWebpackConfig: clientWebpackConfig, + incomingWebpackBuildContext: clientBuildContext, + }); + expect(existsSync).toHaveBeenCalledWith('/dog_park/sentry.client.config.ts'); + }); }); describe('webpack config', () => { @@ -673,7 +686,7 @@ describe('Sentry webpack plugin config', () => { let tempDir: string; beforeAll(() => { - exitsSync.mockImplementation(realExistsSync); + existsSync.mockImplementation(realExistsSync); }); beforeEach(() => { @@ -685,7 +698,7 @@ describe('Sentry webpack plugin config', () => { }); afterAll(() => { - exitsSync.mockImplementation(mockExistsSync); + existsSync.mockImplementation(mockExistsSync); }); it('successfully finds js files', () => {