Skip to content

Commit 3c0d44e

Browse files
ref(nextjs): Make integration utils generic (#3584)
There was some code to set a default integration if the user didn't set it up, but it wasn't generic. This PR makes that code more generic to make it reusable (it's meant to be reused in `client.config.js`, for example).
1 parent 205a364 commit 3c0d44e

File tree

5 files changed

+111
-112
lines changed

5 files changed

+111
-112
lines changed

packages/nextjs/src/index.server.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { RewriteFrames } from '@sentry/integrations';
12
import { configureScope, init as nodeInit } from '@sentry/node';
23

34
import { instrumentServer } from './utils/instrumentServer';
45
import { MetadataBuilder } from './utils/metadataBuilder';
56
import { NextjsOptions } from './utils/nextjsOptions';
6-
import { defaultRewriteFrames, getFinalServerIntegrations } from './utils/serverIntegrations';
7+
import { addIntegration } from './utils/userIntegrations';
78

89
export * from '@sentry/node';
910

@@ -16,12 +17,7 @@ export function init(options: NextjsOptions): void {
1617
const metadataBuilder = new MetadataBuilder(options, ['nextjs', 'node']);
1718
metadataBuilder.addSdkMetadata();
1819
options.environment = options.environment || process.env.NODE_ENV;
19-
if (options.integrations) {
20-
options.integrations = getFinalServerIntegrations(options.integrations);
21-
} else {
22-
options.integrations = [defaultRewriteFrames];
23-
}
24-
20+
addServerIntegrations(options);
2521
// Right now we only capture frontend sessions for Next.js
2622
options.autoSessionTracking = false;
2723

@@ -31,6 +27,23 @@ export function init(options: NextjsOptions): void {
3127
});
3228
}
3329

30+
const SOURCEMAP_FILENAME_REGEX = /^.*\/\.next\//;
31+
32+
const defaultRewriteFramesIntegration = new RewriteFrames({
33+
iteratee: frame => {
34+
frame.filename = frame.filename?.replace(SOURCEMAP_FILENAME_REGEX, 'app:///_next/');
35+
return frame;
36+
},
37+
});
38+
39+
function addServerIntegrations(options: NextjsOptions): void {
40+
if (options.integrations) {
41+
options.integrations = addIntegration(defaultRewriteFramesIntegration, options.integrations);
42+
} else {
43+
options.integrations = [defaultRewriteFramesIntegration];
44+
}
45+
}
46+
3447
export { withSentryConfig } from './utils/config';
3548
export { withSentry } from './utils/handlers';
3649

packages/nextjs/src/utils/serverIntegrations.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Integration } from '@sentry/types';
2+
3+
export type UserFunctionIntegrations = (integrations: Integration[]) => Integration[];
4+
type UserIntegrations = Integration[] | UserFunctionIntegrations;
5+
6+
/**
7+
* Retrieves the patched integrations with the provided integration.
8+
*
9+
* The integration must be present in the final user integrations, and they are compared
10+
* by integration name. If the user has defined one, there's nothing to patch; if not,
11+
* the provided integration is added.
12+
*
13+
* @param integration The integration to patch, if necessary.
14+
* @param userIntegrations Integrations defined by the user.
15+
* @returns Final integrations, patched if necessary.
16+
*/
17+
export function addIntegration(integration: Integration, userIntegrations: UserIntegrations): UserIntegrations {
18+
if (Array.isArray(userIntegrations)) {
19+
return addIntegrationToArray(integration, userIntegrations);
20+
} else {
21+
return addIntegrationToFunction(integration, userIntegrations);
22+
}
23+
}
24+
25+
function addIntegrationToArray(integration: Integration, userIntegrations: Integration[]): Integration[] {
26+
if (userIntegrations.map(int => int.name).includes(integration.name)) {
27+
return userIntegrations;
28+
}
29+
return [...userIntegrations, integration];
30+
}
31+
32+
function addIntegrationToFunction(
33+
integration: Integration,
34+
userIntegrationsFunc: UserFunctionIntegrations,
35+
): UserFunctionIntegrations {
36+
const wrapper: UserFunctionIntegrations = defaultIntegrations => {
37+
const userFinalIntegrations = userIntegrationsFunc(defaultIntegrations);
38+
return addIntegrationToArray(integration, userFinalIntegrations);
39+
};
40+
return wrapper;
41+
}

packages/nextjs/test/serverIntegrations.test.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { RewriteFrames } from '@sentry/integrations';
2+
import { Integration } from '@sentry/types';
3+
4+
import { addIntegration, UserFunctionIntegrations } from '../src/utils/userIntegrations';
5+
6+
const testIntegration = new RewriteFrames();
7+
8+
describe('user integrations without any integrations', () => {
9+
test('as an array', () => {
10+
const userIntegrations: Integration[] = [];
11+
// Should get a single integration
12+
let finalIntegrations = addIntegration(testIntegration, userIntegrations);
13+
expect(finalIntegrations).toBeInstanceOf(Array);
14+
finalIntegrations = finalIntegrations as Integration[];
15+
expect(finalIntegrations).toHaveLength(1);
16+
expect(finalIntegrations[0]).toMatchObject(testIntegration);
17+
});
18+
19+
test('as a function', () => {
20+
const userIntegrationFnc: UserFunctionIntegrations = (): Integration[] => [];
21+
// Should get a single integration
22+
const integrationWrapper = addIntegration(testIntegration, userIntegrationFnc);
23+
expect(integrationWrapper).toBeInstanceOf(Function);
24+
const finalIntegrations = (integrationWrapper as UserFunctionIntegrations)([]);
25+
expect(finalIntegrations).toHaveLength(1);
26+
expect(finalIntegrations[0]).toMatchObject(testIntegration);
27+
});
28+
});
29+
30+
describe('user integrations with integrations', () => {
31+
test('as an array', () => {
32+
const userIntegrations = [new RewriteFrames()];
33+
// Should get the same array (with no patches)
34+
const finalIntegrations = addIntegration(testIntegration, userIntegrations);
35+
expect(finalIntegrations).toMatchObject(userIntegrations);
36+
});
37+
38+
test('as a function', () => {
39+
const userIntegrations = [new RewriteFrames()];
40+
const integrationsFnc: UserFunctionIntegrations = (_integrations: Integration[]): Integration[] => {
41+
return userIntegrations;
42+
};
43+
// Should get a function that returns the test integration
44+
let finalIntegrations = addIntegration(testIntegration, integrationsFnc);
45+
expect(typeof finalIntegrations === 'function').toBe(true);
46+
expect(finalIntegrations).toBeInstanceOf(Function);
47+
finalIntegrations = finalIntegrations as UserFunctionIntegrations;
48+
expect(finalIntegrations([])).toMatchObject(userIntegrations);
49+
});
50+
});

0 commit comments

Comments
 (0)