Skip to content

Commit c4034d4

Browse files
authored
feat(nuxt): Register nuxt route provider (#23794)
Registers a route provider for Nuxt in the client plugin, outside the `__SENTRY_TRACING__` guard. Nuxt installs the router before its plugins run, so the plugin can read it straight off `nuxtApp` rather than waiting for a Vue app the way `@sentry/vue` has to. Reuses `createVueRouteProvider` from #23553's successor rather than reimplementing the vue-router resolve handling. Because it sits outside the tracing guard, route parameterization survives when tracing is tree-shaken away. Part of #23556
1 parent de00415 commit c4034d4

3 files changed

Lines changed: 44 additions & 1 deletion

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import * as Sentry from '@sentry/nuxt';
3+
import { onMounted, ref } from 'vue';
4+
5+
const route = ref<string>();
6+
7+
onMounted(() => {
8+
route.value = Sentry.resolveCurrentRoute() ?? 'unresolved';
9+
});
10+
</script>
11+
12+
<template>
13+
<div id="resolved-route">{{ route }}</div>
14+
</template>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
// The route provider reads the router off the Nuxt app, so this fails if the plugin never registers it.
4+
test('resolves the parameterized route through the route provider', async ({ page }) => {
5+
await page.goto('/route-provider/123');
6+
7+
await expect(page.locator('#resolved-route')).toHaveText('/route-provider/:id()');
8+
});

‎packages/nuxt/src/runtime/plugins/sentry.client.ts‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { getClient, GLOBAL_OBJ } from '@sentry/core';
2-
import { browserTracingIntegration, vueIntegration } from '@sentry/vue';
2+
import {
3+
browserTracingIntegration,
4+
_INTERNAL_createVueRouteProvider,
5+
getRouteProvider,
6+
setRouteProvider,
7+
vueIntegration,
8+
} from '@sentry/vue';
39
import { defineNuxtPlugin, isNuxtError } from 'nuxt/app';
410
import type { GlobalObjWithIntegrationOptions } from '../../client/vueIntegration';
511
import { reportNuxtError } from '../utils';
@@ -28,13 +34,28 @@ interface VueRouter {
2834
beforeEach: (fn: (to: Route, from: Route, next?: () => void) => void) => void;
2935
}
3036

37+
type VueRouteProviderRouter = ReturnType<Parameters<typeof _INTERNAL_createVueRouteProvider>[0]>;
38+
3139
// Tree-shakable guard to remove all code related to tracing
3240
declare const __SENTRY_TRACING__: boolean;
3341

3442
export default defineNuxtPlugin({
3543
name: 'sentry-client-integrations',
3644
dependsOn: ['sentry-client-config'],
3745
async setup(nuxtApp) {
46+
// Registered outside the tracing guard, because route parameterization should not depend on
47+
// tracing: anything that needs a route name (bfcache metrics, web vitals) can resolve one even
48+
// when tracing is tree-shaken away. Nuxt installs the router before its plugins run, so unlike
49+
// `@sentry/vue` this can read it straight off `nuxtApp`.
50+
const client = getClient();
51+
// A `routeProvider` passed to `Sentry.init` is the user's choice, so it is left in place.
52+
if (client && '$router' in nuxtApp && !getRouteProvider(client)) {
53+
setRouteProvider(
54+
_INTERNAL_createVueRouteProvider(() => nuxtApp.$router as VueRouteProviderRouter),
55+
client,
56+
);
57+
}
58+
3859
// This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false", in which case everything inside
3960
// will get tree-shaken away
4061
if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) {

0 commit comments

Comments
 (0)