diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index a61e49fe758f..428797f7c223 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -10,7 +10,7 @@ import * as path from 'path'; import { isBuild } from './utils/isBuild'; import { buildMetadata } from './utils/metadata'; import { NextjsOptions } from './utils/nextjsOptions'; -import { addOrUpdateIntegration } from './utils/userIntegrations'; +import { addOrUpdateIntegration, updateIntegration } from './utils/userIntegrations'; export * from '@sentry/node'; export { captureUnderscoreErrorException } from './utils/_error'; @@ -118,11 +118,6 @@ function addServerIntegrations(options: NextjsOptions): void { }); integrations = addOrUpdateIntegration(defaultRewriteFramesIntegration, integrations); - const nativeBehaviourOnUncaughtException = new Integrations.OnUncaughtException(); - integrations = addOrUpdateIntegration(nativeBehaviourOnUncaughtException, integrations, { - _options: { exitEvenIfOtherHandlersAreRegistered: false }, - }); - if (hasTracingEnabled(options)) { const defaultHttpTracingIntegration = new Integrations.Http({ tracing: true }); integrations = addOrUpdateIntegration(defaultHttpTracingIntegration, integrations, { @@ -130,6 +125,10 @@ function addServerIntegrations(options: NextjsOptions): void { }); } + updateIntegration('OnUncaughtException', integrations, { + _options: { exitEvenIfOtherHandlersAreRegistered: false }, + }); + options.integrations = integrations; } diff --git a/packages/nextjs/src/utils/userIntegrations.ts b/packages/nextjs/src/utils/userIntegrations.ts index 5f9e21b6b0f2..d07ed51bbb2d 100644 --- a/packages/nextjs/src/utils/userIntegrations.ts +++ b/packages/nextjs/src/utils/userIntegrations.ts @@ -82,3 +82,50 @@ function addOrUpdateIntegrationInFunction( }; return wrapper; } + +/** + * Updates the internal fields of a given integration in an integration array or integration function. + * + * @param defaultIntegrationInstance The `name` field of the integration to patch. + * @param userIntegrations Integrations defined by the user. + * @param forcedOptions Options with which to patch an existing integration instance. + * @returns A final integrations array. + */ +export function updateIntegration( + integrationName: string, + userIntegrations: UserIntegrations, + forcedOptions: ForcedIntegrationOptions, +): UserIntegrations { + return Array.isArray(userIntegrations) + ? updateIntegrationInArray(integrationName, userIntegrations, forcedOptions) + : updateIntegrationInFunction(integrationName, userIntegrations, forcedOptions); +} + +function updateIntegrationInFunction( + integrationName: string, + userIntegrationsFunc: UserIntegrationsFunction, + forcedOptions: ForcedIntegrationOptions, +): UserIntegrationsFunction { + const wrapper: UserIntegrationsFunction = defaultIntegrations => { + const userFinalIntegrations = userIntegrationsFunc(defaultIntegrations); + updateIntegrationInArray(integrationName, userFinalIntegrations, forcedOptions); + return userFinalIntegrations; + }; + return wrapper; +} + +function updateIntegrationInArray( + integrationName: string, + userIntegrations: Integration[], + forcedOptions: ForcedIntegrationOptions, +): Integration[] { + const userInstance = userIntegrations.find(integration => integration.name === integrationName); + + if (userInstance) { + for (const [keyPath, value] of Object.entries(forcedOptions)) { + setNestedKey(userInstance, keyPath, value); + } + } + + return userIntegrations; +} diff --git a/packages/nextjs/test/utils/userIntegrations.test.ts b/packages/nextjs/test/utils/userIntegrations.test.ts index aa2aeca12e61..40a81a6b69fe 100644 --- a/packages/nextjs/test/utils/userIntegrations.test.ts +++ b/packages/nextjs/test/utils/userIntegrations.test.ts @@ -1,50 +1,105 @@ import { RewriteFrames } from '@sentry/integrations'; import { Integration } from '@sentry/types'; -import { addOrUpdateIntegration, UserIntegrationsFunction } from '../../src/utils/userIntegrations'; +import { addOrUpdateIntegration, updateIntegration, UserIntegrationsFunction } from '../../src/utils/userIntegrations'; const testIntegration = new RewriteFrames(); -describe('user integrations without any integrations', () => { - test('as an array', () => { - const userIntegrations: Integration[] = []; - // Should get a single integration - let finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations); - expect(finalIntegrations).toBeInstanceOf(Array); - finalIntegrations = finalIntegrations as Integration[]; - expect(finalIntegrations).toHaveLength(1); - expect(finalIntegrations[0]).toMatchObject(testIntegration); +describe('addOrUpdateIntegration()', () => { + describe('user integrations without any integrations', () => { + test('as an array', () => { + const userIntegrations: Integration[] = []; + // Should get a single integration + let finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations); + expect(finalIntegrations).toBeInstanceOf(Array); + finalIntegrations = finalIntegrations as Integration[]; + expect(finalIntegrations).toHaveLength(1); + expect(finalIntegrations[0]).toMatchObject(testIntegration); + }); + + test('as a function', () => { + const userIntegrationFnc: UserIntegrationsFunction = (): Integration[] => []; + // Should get a single integration + const integrationWrapper = addOrUpdateIntegration(testIntegration, userIntegrationFnc); + expect(integrationWrapper).toBeInstanceOf(Function); + const finalIntegrations = (integrationWrapper as UserIntegrationsFunction)([]); + expect(finalIntegrations).toHaveLength(1); + expect(finalIntegrations[0]).toMatchObject(testIntegration); + }); }); - test('as a function', () => { - const userIntegrationFnc: UserIntegrationsFunction = (): Integration[] => []; - // Should get a single integration - const integrationWrapper = addOrUpdateIntegration(testIntegration, userIntegrationFnc); - expect(integrationWrapper).toBeInstanceOf(Function); - const finalIntegrations = (integrationWrapper as UserIntegrationsFunction)([]); - expect(finalIntegrations).toHaveLength(1); - expect(finalIntegrations[0]).toMatchObject(testIntegration); + describe('user integrations with integrations', () => { + test('as an array', () => { + const userIntegrations = [new RewriteFrames()]; + // Should get the same array (with no patches) + const finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations); + expect(finalIntegrations).toMatchObject(userIntegrations); + }); + + test('as a function', () => { + const userIntegrations = [new RewriteFrames()]; + const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => { + return userIntegrations; + }; + // Should get a function that returns the test integration + let finalIntegrations = addOrUpdateIntegration(testIntegration, integrationsFnc); + expect(typeof finalIntegrations === 'function').toBe(true); + expect(finalIntegrations).toBeInstanceOf(Function); + finalIntegrations = finalIntegrations as UserIntegrationsFunction; + expect(finalIntegrations([])).toMatchObject(userIntegrations); + }); }); }); -describe('user integrations with integrations', () => { - test('as an array', () => { - const userIntegrations = [new RewriteFrames()]; - // Should get the same array (with no patches) - const finalIntegrations = addOrUpdateIntegration(testIntegration, userIntegrations); - expect(finalIntegrations).toMatchObject(userIntegrations); +describe('updateIntegration()', () => { + describe('integrations function', () => { + test('should update an integration if it exists', () => { + const intagration = new RewriteFrames() as any; + const userIntegrations = [intagration]; + const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => { + return userIntegrations; + }; + // Should get a function that returns the test integration + let finalIntegrations = updateIntegration('RewriteFrames', integrationsFnc, { _option: true }); + expect(typeof finalIntegrations === 'function').toBe(true); + finalIntegrations = finalIntegrations as UserIntegrationsFunction; + const evaluatedIntegrations = finalIntegrations([]); + expect(evaluatedIntegrations).toMatchObject(userIntegrations); + expect(intagration._option).toBe(true); + }); + + test("should not update an integration if it doesn't exist", () => { + const intagration = new RewriteFrames() as any; + const userIntegrations = [intagration]; + const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => { + return userIntegrations; + }; + let finalIntegrations = updateIntegration('UncaughtException', integrationsFnc, { _option: true }); + expect(typeof finalIntegrations === 'function').toBe(true); + finalIntegrations = finalIntegrations as UserIntegrationsFunction; + const evaluatedIntegrations = finalIntegrations([]); + expect(evaluatedIntegrations).toMatchObject(userIntegrations); + expect(intagration._option).not.toBeDefined(); + }); }); - test('as a function', () => { - const userIntegrations = [new RewriteFrames()]; - const integrationsFnc: UserIntegrationsFunction = (_integrations: Integration[]): Integration[] => { - return userIntegrations; - }; - // Should get a function that returns the test integration - let finalIntegrations = addOrUpdateIntegration(testIntegration, integrationsFnc); - expect(typeof finalIntegrations === 'function').toBe(true); - expect(finalIntegrations).toBeInstanceOf(Function); - finalIntegrations = finalIntegrations as UserIntegrationsFunction; - expect(finalIntegrations([])).toMatchObject(userIntegrations); + describe('integrations array', () => { + test('should update an integration if it exists', () => { + const intagration = new RewriteFrames() as any; + const userIntegrations = [intagration]; + const finalIntegrations = updateIntegration('RewriteFrames', userIntegrations, { _option: true }); + expect(finalIntegrations).toBeInstanceOf(Array); + expect(finalIntegrations).toMatchObject(userIntegrations); + expect(intagration._option).toBe(true); + }); + + test("should not update an integration if it doesn't exist", () => { + const intagration = new RewriteFrames() as any; + const userIntegrations = [intagration]; + const finalIntegrations = updateIntegration('UncaughtException', userIntegrations, { _option: true }); + expect(finalIntegrations).toBeInstanceOf(Array); + expect(finalIntegrations).toMatchObject(userIntegrations); + expect(intagration._option).not.toBeDefined(); + }); }); });