11import { hasTracingEnabled } from '@sentry/core' ;
2- import { RewriteFrames } from '@sentry/integrations' ;
32import type { BrowserOptions } from '@sentry/react' ;
43import {
5- BrowserTracing ,
6- Integrations ,
7- defaultRequestInstrumentationOptions ,
4+ Integrations as OriginalIntegrations ,
85 getCurrentScope ,
6+ getDefaultIntegrations as getReactDefaultIntegrations ,
97 init as reactInit ,
108} from '@sentry/react' ;
11- import type { EventProcessor } from '@sentry/types' ;
12- import { addOrUpdateIntegration } from '@sentry/utils' ;
9+ import type { EventProcessor , Integration } from '@sentry/types' ;
1310
1411import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor' ;
1512import { getVercelEnv } from '../common/getVercelEnv' ;
1613import { buildMetadata } from '../common/metadata' ;
17- import { nextRouterInstrumentation } from './routing/nextRoutingInstrumentation' ;
14+ import { BrowserTracing } from './browserTracingIntegration' ;
15+ import { rewriteFramesIntegration } from './rewriteFramesIntegration' ;
1816import { applyTunnelRouteOption } from './tunnelRoute' ;
1917
2018export * from '@sentry/react' ;
2119export { nextRouterInstrumentation } from './routing/nextRoutingInstrumentation' ;
2220export { captureUnderscoreErrorException } from '../common/_error' ;
2321
24- export { Integrations } ;
22+ export const Integrations = {
23+ ...OriginalIntegrations ,
24+ BrowserTracing,
25+ } ;
2526
2627// Previously we expected users to import `BrowserTracing` like this:
2728//
@@ -33,27 +34,24 @@ export { Integrations };
3334//
3435// import { BrowserTracing } from '@sentry/nextjs';
3536// const instance = new BrowserTracing();
36- export { BrowserTracing } ;
37+ export { BrowserTracing , rewriteFramesIntegration } ;
3738
3839// Treeshakable guard to remove all code related to tracing
3940declare const __SENTRY_TRACING__ : boolean ;
4041
41- const globalWithInjectedValues = global as typeof global & {
42- __rewriteFramesAssetPrefixPath__ : string ;
43- } ;
44-
4542/** Inits the Sentry NextJS SDK on the browser with the React SDK. */
4643export function init ( options : BrowserOptions ) : void {
4744 const opts = {
4845 environment : getVercelEnv ( true ) || process . env . NODE_ENV ,
46+ defaultIntegrations : getDefaultIntegrations ( options ) ,
4947 ...options ,
5048 } ;
5149
50+ fixBrowserTracingIntegration ( opts ) ;
51+
5252 applyTunnelRouteOption ( opts ) ;
5353 buildMetadata ( opts , [ 'nextjs' , 'react' ] ) ;
5454
55- addClientIntegrations ( opts ) ;
56-
5755 reactInit ( opts ) ;
5856
5957 const scope = getCurrentScope ( ) ;
@@ -68,72 +66,53 @@ export function init(options: BrowserOptions): void {
6866 }
6967}
7068
71- function addClientIntegrations ( options : BrowserOptions ) : void {
72- let integrations = options . integrations || [ ] ;
73-
74- // This value is injected at build time, based on the output directory specified in the build config. Though a default
75- // is set there, we set it here as well, just in case something has gone wrong with the injection.
76- const assetPrefixPath = globalWithInjectedValues . __rewriteFramesAssetPrefixPath__ || '' ;
77-
78- // eslint-disable-next-line deprecation/deprecation
79- const defaultRewriteFramesIntegration = new RewriteFrames ( {
80- // Turn `<origin>/<path>/_next/static/...` into `app:///_next/static/...`
81- iteratee : frame => {
82- try {
83- const { origin } = new URL ( frame . filename as string ) ;
84- frame . filename = frame . filename ?. replace ( origin , 'app://' ) . replace ( assetPrefixPath , '' ) ;
85- } catch ( err ) {
86- // Filename wasn't a properly formed URL, so there's nothing we can do
87- }
88-
89- // We need to URI-decode the filename because Next.js has wildcard routes like "/users/[id].js" which show up as "/users/%5id%5.js" in Error stacktraces.
90- // The corresponding sources that Next.js generates have proper brackets so we also need proper brackets in the frame so that source map resolving works.
91- if ( frame . filename && frame . filename . startsWith ( 'app:///_next' ) ) {
92- frame . filename = decodeURI ( frame . filename ) ;
93- }
94-
95- if (
96- frame . filename &&
97- frame . filename . match (
98- / ^ a p p : \/ \/ \/ _ n e x t \/ s t a t i c \/ c h u n k s \/ ( m a i n - | m a i n - a p p - | p o l y f i l l s - | w e b p a c k - | f r a m e w o r k - | f r a m e w o r k \. ) [ 0 - 9 a - f ] + \. j s $ / ,
99- )
100- ) {
101- // We don't care about these frames. It's Next.js internal code.
102- frame . in_app = false ;
103- }
104-
105- return frame ;
106- } ,
107- } ) ;
108- integrations = addOrUpdateIntegration ( defaultRewriteFramesIntegration , integrations ) ;
69+ // TODO v8: Remove this again
70+ // We need to handle BrowserTracing passed to `integrations` that comes from `@sentry/tracing`, not `@sentry/sveltekit` :(
71+ function fixBrowserTracingIntegration ( options : BrowserOptions ) : void {
72+ const { integrations } = options ;
73+ if ( ! integrations ) {
74+ return ;
75+ }
76+
77+ if ( Array . isArray ( integrations ) ) {
78+ options . integrations = maybeUpdateBrowserTracingIntegration ( integrations ) ;
79+ } else {
80+ options . integrations = defaultIntegrations => {
81+ const userFinalIntegrations = integrations ( defaultIntegrations ) ;
82+
83+ return maybeUpdateBrowserTracingIntegration ( userFinalIntegrations ) ;
84+ } ;
85+ }
86+ }
87+
88+ function maybeUpdateBrowserTracingIntegration ( integrations : Integration [ ] ) : Integration [ ] {
89+ const browserTracing = integrations . find ( integration => integration . name === 'BrowserTracing' ) ;
90+ // If BrowserTracing was added, but it is not our forked version,
91+ // replace it with our forked version with the same options
92+ if ( browserTracing && ! ( browserTracing instanceof BrowserTracing ) ) {
93+ const options : ConstructorParameters < typeof BrowserTracing > [ 0 ] = ( browserTracing as BrowserTracing ) . options ;
94+ // These two options are overwritten by the custom integration
95+ delete options . routingInstrumentation ;
96+ // eslint-disable-next-line deprecation/deprecation
97+ delete options . tracingOrigins ;
98+ integrations [ integrations . indexOf ( browserTracing ) ] = new BrowserTracing ( options ) ;
99+ }
100+
101+ return integrations ;
102+ }
103+
104+ function getDefaultIntegrations ( options : BrowserOptions ) : Integration [ ] {
105+ const customDefaultIntegrations = [ ...getReactDefaultIntegrations ( options ) , rewriteFramesIntegration ( ) ] ;
109106
110107 // This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false", in which case everything inside
111108 // will get treeshaken away
112109 if ( typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__ ) {
113110 if ( hasTracingEnabled ( options ) ) {
114- const defaultBrowserTracingIntegration = new BrowserTracing ( {
115- // eslint-disable-next-line deprecation/deprecation
116- tracingOrigins :
117- process . env . NODE_ENV === 'development'
118- ? [
119- // Will match any URL that contains "localhost" but not "webpack.hot-update.json" - The webpack dev-server
120- // has cors and it doesn't like extra headers when it's accessed from a different URL.
121- // TODO(v8): Ideally we rework our tracePropagationTargets logic so this hack won't be necessary anymore (see issue #9764)
122- / ^ (? = .* l o c a l h o s t ) (? ! .* w e b p a c k \. h o t - u p d a t e \. j s o n ) .* / ,
123- / ^ \/ (? ! \/ ) / ,
124- ]
125- : // eslint-disable-next-line deprecation/deprecation
126- [ ...defaultRequestInstrumentationOptions . tracingOrigins , / ^ ( a p i \/ ) / ] ,
127- routingInstrumentation : nextRouterInstrumentation ,
128- } ) ;
129-
130- integrations = addOrUpdateIntegration ( defaultBrowserTracingIntegration , integrations , {
131- 'options.routingInstrumentation' : nextRouterInstrumentation ,
132- } ) ;
111+ customDefaultIntegrations . push ( new BrowserTracing ( ) ) ;
133112 }
134113 }
135114
136- options . integrations = integrations ;
115+ return customDefaultIntegrations ;
137116}
138117
139118/**
0 commit comments