Skip to content

Commit 89d893e

Browse files
committed
feat(vue): Register the route provider for Vue 2
1 parent a6c8975 commit 89d893e

4 files changed

Lines changed: 44 additions & 16 deletions

File tree

‎packages/vue/src/integration.ts‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DEFAULT_HOOKS, DEFAULT_ROOT_SPAN_TIMEOUT } from './constants';
44
import { DEBUG_BUILD } from './debug-build';
55
import { attachErrorHandler } from './errorhandler';
66
import { instrumentAppMountWithoutMixin } from './rootInstrumentation';
7-
import { createVueRouteProvider, getRouterFromApp } from './routeProvider';
7+
import { captureRouterFromVue, createVueRouteProvider, getRouterFromApp } from './routeProvider';
88
import { createTracingMixins } from './tracing';
99
import type { Options, TracingOptions, Vue, VueOptions } from './types';
1010

@@ -40,16 +40,14 @@ export const vueIntegration = defineIntegration((integrationOptions: Partial<Vue
4040
return;
4141
}
4242

43-
if (options.app) {
44-
const { app } = options;
45-
// Read per call because `app.use(router)` may run after `init`. A user's `routeProvider` wins.
46-
if (!getRouteProvider(client)) {
47-
setRouteProvider(
48-
createVueRouteProvider(() => getRouterFromApp(app)),
49-
client,
50-
);
51-
}
43+
// The router is looked up lazily because it may be installed after `init`. A user's `routeProvider` wins.
44+
if (!getRouteProvider(client)) {
45+
const { app, Vue } = options;
46+
const getRouter = app ? () => getRouterFromApp(app) : captureRouterFromVue(Vue!);
47+
setRouteProvider(createVueRouteProvider(getRouter), client);
48+
}
5249

50+
if (options.app) {
5351
const apps = Array.isArray(options.app) ? options.app : [options.app];
5452
apps.forEach(app => vueInit(app, options));
5553
} else if (options.Vue) {

‎packages/vue/src/routeProvider.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ export function getRouterFromApp(app: Vue | Vue[] | undefined): InstalledRouter
5252
return firstApp?.config?.globalProperties?.$router;
5353
}
5454

55+
/**
56+
* Vue 2 only exposes the router on instances, not on the constructor passed to `init`, so this picks it
57+
* up from the root instance (`new Vue({ router })`) as it is created.
58+
*/
59+
export function captureRouterFromVue(Vue: Vue): () => InstalledRouter | undefined {
60+
let router: InstalledRouter | undefined;
61+
Vue.mixin({
62+
beforeCreate(this: { $options: { router?: InstalledRouter } }) {
63+
router = router || this.$options.router;
64+
},
65+
});
66+
67+
return () => router;
68+
}
69+
5570
/**
5671
* `resolve` matches the router's own location rather than the browser's: the hash in hash mode, and
5772
* the path without the router's base otherwise. Mirrors vue-router's `createCurrentLocation`.

‎packages/vue/test/integration/mixinRegistration.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ describe('tracing mixin registration', () => {
160160
it('registers the tracing mixin on the constructor passed as `Vue` (Vue 2 setup)', ({ app, initSentry }) => {
161161
initSentry({ sdk: { app: undefined, Vue: app } });
162162

163-
expect(getRegisteredMixins(app)).toHaveLength(1);
163+
// The tracing mixin, plus the one that picks up the router for the route provider.
164+
expect(getRegisteredMixins(app)).toHaveLength(2);
164165
});
165166
});
166167

‎packages/vue/test/sdk.test.ts‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ import type { Vue } from '../src/types';
77

88
const DSN = 'https://public@dsn.ingest.sentry.io/1337';
99

10+
const router = { resolve: () => ({ matched: [{ path: '/users/:id' }] }) };
11+
1012
const app = {
11-
config: {
12-
globalProperties: {
13-
$router: { resolve: () => ({ matched: [{ path: '/users/:id' }] }) },
14-
},
15-
},
13+
config: { globalProperties: { $router: router } },
1614
mixin: vi.fn(),
1715
} as unknown as Vue;
1816

@@ -34,6 +32,22 @@ describe('route provider registration', () => {
3432
expect(resolveRoute('https://example.com/users/42', client)).toBe('/users/:id');
3533
});
3634

35+
it('registers a route provider that picks the router up from the Vue 2 root instance', () => {
36+
let beforeCreate: (this: unknown) => void = () => {};
37+
const Vue2 = {
38+
config: {},
39+
mixin: (mixin: { beforeCreate: typeof beforeCreate }) => {
40+
beforeCreate = mixin.beforeCreate;
41+
},
42+
} as unknown as Vue;
43+
const client = init({ dsn: DSN, Vue: Vue2, defaultIntegrations: false, integrations: [vueIntegration()] });
44+
45+
expect(resolveRoute('https://example.com/users/42', client)).toBeUndefined();
46+
47+
beforeCreate.call({ $options: { router } });
48+
expect(resolveRoute('https://example.com/users/42', client)).toBe('/users/:id');
49+
});
50+
3751
it('does not register a route provider without an app to read the router from', () => {
3852
const client = init({ dsn: DSN, defaultIntegrations: false, integrations: [vueIntegration()] });
3953

0 commit comments

Comments
 (0)