diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/sentry.client.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-min/sentry.client.config.ts index 7547bafa6618..9a9566051452 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/sentry.client.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/sentry.client.config.ts @@ -6,5 +6,11 @@ Sentry.init({ dsn: useRuntimeConfig().public.sentry.dsn, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, - trackComponents: true, + integrations: [ + Sentry.vueIntegration({ + tracingOptions: { + trackComponents: true, + }, + }), + ], }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts index 7547bafa6618..9a9566051452 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts @@ -6,5 +6,11 @@ Sentry.init({ dsn: useRuntimeConfig().public.sentry.dsn, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, - trackComponents: true, + integrations: [ + Sentry.vueIntegration({ + tracingOptions: { + trackComponents: true, + }, + }), + ], }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/sentry.client.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/sentry.client.config.ts index dd2183162db9..e8b628595975 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/sentry.client.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/sentry.client.config.ts @@ -6,7 +6,6 @@ Sentry.init({ dsn: useRuntimeConfig().public.sentry.dsn, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, - trackComponents: true, integrations: [ Sentry.piniaIntegration(usePinia(), { actionTransformer: action => `Transformed: ${action}`, @@ -15,5 +14,10 @@ Sentry.init({ ...state, }), }), + Sentry.vueIntegration({ + tracingOptions: { + trackComponents: true, + }, + }), ], }); diff --git a/packages/nuxt/src/client/index.ts b/packages/nuxt/src/client/index.ts index 849c305a22e3..d476348ed937 100644 --- a/packages/nuxt/src/client/index.ts +++ b/packages/nuxt/src/client/index.ts @@ -2,3 +2,4 @@ export * from '@sentry/vue'; export { init } from './sdk'; export { piniaIntegration } from './piniaIntegration'; +export { vueIntegration } from './vueIntegration'; diff --git a/packages/nuxt/src/client/vueIntegration.ts b/packages/nuxt/src/client/vueIntegration.ts new file mode 100644 index 000000000000..77f1b7fb3d51 --- /dev/null +++ b/packages/nuxt/src/client/vueIntegration.ts @@ -0,0 +1,36 @@ +import { GLOBAL_OBJ, defineIntegration } from '@sentry/core'; +import type { VueIntegrationOptions } from '@sentry/vue'; + +type Options = Omit< + VueIntegrationOptions, + | 'app' + | 'Vue' + // TODO(v9): Should be removed from parent type so we can remove it here + | 'hooks' + // TODO(v9): Should be removed from parent type so we can remove it here + | 'timeout' + // TODO(v9): Should be removed from parent type so we can remove it here + | 'trackComponents' +>; + +// Since the options object needs to cross the boundary between some builds (i.e. the nuxt module build and our client +// SDK build) we cannot use a getter that is exported from here. Instead we'll pass the options object through a global +// to the module. +export type GlobalObjWithIntegrationOptions = { _sentryNuxtVueIntegrationOptions?: Options }; + +// The vue integration is actually set up in the Sentry Client Module. There it is set up as soon as the nuxt app object is available. +// However, we need to export the vueIntegration from the Client SDK. This means all this integration does is store away +// its options for the Sentry Client Module to pick them up when initializing the actual vueIntegration. + +/** + * Add additional error and span instrumentation specialized for Vue. + */ +export const vueIntegration = defineIntegration((options: Options = {}) => { + return { + // NOTE: This name is different from the original vueIntegration's name. + name: 'NuxtVueIntegration', + setup() { + (GLOBAL_OBJ as GlobalObjWithIntegrationOptions)._sentryNuxtVueIntegrationOptions = options; + }, + }; +}); diff --git a/packages/nuxt/src/runtime/plugins/sentry.client.ts b/packages/nuxt/src/runtime/plugins/sentry.client.ts index b89a2fa87a8d..39d62737f645 100644 --- a/packages/nuxt/src/runtime/plugins/sentry.client.ts +++ b/packages/nuxt/src/runtime/plugins/sentry.client.ts @@ -1,6 +1,7 @@ -import { getClient } from '@sentry/core'; +import { GLOBAL_OBJ, getClient } from '@sentry/core'; import { browserTracingIntegration, vueIntegration } from '@sentry/vue'; import { defineNuxtPlugin } from 'nuxt/app'; +import type { GlobalObjWithIntegrationOptions } from '../../client/vueIntegration'; import { reportNuxtError } from '../utils'; // --- Types are copied from @sentry/vue (so it does not need to be exported) --- @@ -53,7 +54,14 @@ export default defineNuxtPlugin({ // Adding the Vue integration without the Vue error handler // Nuxt is registering their own error handler, which is unset after hydration: https://github.com/nuxt/nuxt/blob/d3fdbcaac6cf66d21e25d259390d7824696f1a87/packages/nuxt/src/app/entry.ts#L64-L73 // We don't want to wrap the existing error handler, as it leads to a 500 error: https://github.com/getsentry/sentry-javascript/issues/12515 - sentryClient.addIntegration(vueIntegration({ app: vueApp, attachErrorHandler: false })); + sentryClient.addIntegration( + vueIntegration({ + // We pick up the options from the "fake" vueIntegration exported by the SDK. + ...(GLOBAL_OBJ as GlobalObjWithIntegrationOptions)._sentryNuxtVueIntegrationOptions, + app: vueApp, + attachErrorHandler: false, + }), + ); } }); diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 096b7a2144e5..7e57d55865f7 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -5,4 +5,5 @@ export { browserTracingIntegration } from './browserTracingIntegration'; export { attachErrorHandler } from './errorhandler'; export { createTracingMixins } from './tracing'; export { vueIntegration } from './integration'; +export type { VueIntegrationOptions } from './integration'; export { createSentryPiniaPlugin } from './pinia'; diff --git a/packages/vue/src/integration.ts b/packages/vue/src/integration.ts index 22f394d72720..5c30a956304b 100644 --- a/packages/vue/src/integration.ts +++ b/packages/vue/src/integration.ts @@ -19,6 +19,8 @@ const DEFAULT_CONFIG: VueOptions = { const INTEGRATION_NAME = 'Vue'; +export type VueIntegrationOptions = Partial; + export const vueIntegration = defineIntegration((integrationOptions: Partial = {}) => { return { name: INTEGRATION_NAME,