Skip to content

feat(nextjs): Allow custom path to sentry.client.config.ts #4187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/nextjs/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
8 changes: 5 additions & 3 deletions packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -119,6 +119,7 @@ export function constructWebpackConfigFunction(
async function addSentryToEntryProperty(
currentEntryProperty: WebpackEntryProperty,
buildContext: BuildContext,
userNextConfig: Partial<NextConfigObject>,
): Promise<EntryPropertyObject> {
// 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
Expand All @@ -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}`];
Expand Down
19 changes: 16 additions & 3 deletions packages/nextjs/test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -673,7 +686,7 @@ describe('Sentry webpack plugin config', () => {
let tempDir: string;

beforeAll(() => {
exitsSync.mockImplementation(realExistsSync);
existsSync.mockImplementation(realExistsSync);
});

beforeEach(() => {
Expand All @@ -685,7 +698,7 @@ describe('Sentry webpack plugin config', () => {
});

afterAll(() => {
exitsSync.mockImplementation(mockExistsSync);
existsSync.mockImplementation(mockExistsSync);
});

it('successfully finds js files', () => {
Expand Down