Skip to content

Commit c8fcfe8

Browse files
committed
feat(vue): Expose initVueApp method to initialize vue app later
1 parent 945d873 commit c8fcfe8

File tree

5 files changed

+126
-14
lines changed

5 files changed

+126
-14
lines changed

packages/vue/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export * from '@sentry/browser';
22

3-
export { init } from './sdk';
3+
export { init, initVueApp } from './sdk';
44
export { vueRouterInstrumentation } from './router';
55
export { attachErrorHandler } from './errorhandler';
66
export { createTracingMixins } from './tracing';

packages/vue/src/sdk.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { init as browserInit, SDK_VERSION } from '@sentry/browser';
2-
import { hasTracingEnabled } from '@sentry/core';
3-
import { arrayify, GLOBAL_OBJ } from '@sentry/utils';
2+
import { getCurrentHub, hasTracingEnabled } from '@sentry/core';
3+
import { arrayify, GLOBAL_OBJ, logger } from '@sentry/utils';
44

55
import { DEFAULT_HOOKS } from './constants';
66
import { attachErrorHandler } from './errorhandler';
@@ -43,7 +43,7 @@ export function init(
4343

4444
browserInit(options);
4545

46-
if (!options.Vue && !options.app) {
46+
if (!options.Vue && !options.app && options.app !== false) {
4747
// eslint-disable-next-line no-console
4848
console.warn(
4949
`[@sentry/vue]: Misconfigured SDK. Vue specific errors will not be captured.
@@ -61,6 +61,22 @@ Update your \`Sentry.init\` call with an appropriate config option:
6161
}
6262
}
6363

64+
/**
65+
* Initialize Vue-specific error monitoring for a given Vue app.
66+
*/
67+
export function initVueApp(app: Vue): void {
68+
const client = getCurrentHub().getClient();
69+
const options = client && (client.getOptions() as Options);
70+
71+
if (options) {
72+
vueInit(app, options);
73+
} else if (__DEBUG_BUILD__) {
74+
logger.warn(
75+
'[@sentry/vue]: Cannot initialize as no Client available. Make sure to call `Sentry.init` before calling `initVueApp()`.',
76+
);
77+
}
78+
}
79+
6480
const vueInit = (app: Vue, options: Options): void => {
6581
// Check app is not mounted yet - should be mounted _after_ init()!
6682
// This is _somewhat_ private, but in the case that this doesn't exist we simply ignore it

packages/vue/src/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ export interface Options extends TracingOptions, BrowserOptions {
2929
/** Vue constructor to be used inside the integration (as imported by `import Vue from 'vue'` in Vue2) */
3030
Vue?: Vue;
3131

32-
/** Vue app instance(s) to be used inside the integration (as generated by `createApp` in Vue3 ) */
33-
app?: Vue | Vue[];
32+
/**
33+
* Vue app instance(s) to be used inside the integration (as generated by `createApp` in Vue3 )
34+
* Set this to `false` to indicate you are purposefully _not_ setting up a Vue app right now,
35+
* e.g. if you want to manually call `initVueApp` later.
36+
*/
37+
app?: Vue | Vue[] | false;
3438

3539
/**
3640
* When set to `false`, Sentry will suppress reporting of all props data

packages/vue/test/integration/init.test.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,17 @@ import { createApp } from 'vue';
33
import * as Sentry from './../../src';
44

55
describe('Sentry.init', () => {
6-
let _consoleWarn: any;
7-
let warnings: string[] = [];
6+
let warnings: unknown[] = [];
87

98
beforeEach(() => {
109
warnings = [];
11-
// eslint-disable-next-line no-console
12-
_consoleWarn = console.warn;
13-
// eslint-disable-next-line no-console
14-
console.warn = jest.fn((message: string) => {
10+
jest.spyOn(console, 'warn').mockImplementation((message: unknown) => {
1511
warnings.push(message);
1612
});
1713
});
1814

1915
afterEach(() => {
20-
// eslint-disable-next-line no-console
21-
console.warn = _consoleWarn;
16+
jest.clearAllMocks();
2217
});
2318

2419
it('does not warn when correctly setup (Vue 3)', () => {
@@ -90,4 +85,20 @@ Update your \`Sentry.init\` call with an appropriate config option:
9085
\`app\` (Application Instance - Vue 3) or \`Vue\` (Vue Constructor - Vue 2).`,
9186
]);
9287
});
88+
89+
it('does not warn when passing app=false', () => {
90+
const el = document.createElement('div');
91+
const app = createApp({
92+
template: '<div>hello</div>',
93+
});
94+
95+
Sentry.init({
96+
app: false,
97+
defaultIntegrations: false,
98+
});
99+
100+
app.mount(el);
101+
102+
expect(warnings).toEqual([]);
103+
});
93104
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { logger } from '@sentry/utils';
2+
import { createApp } from 'vue';
3+
4+
import * as Sentry from '../../src';
5+
import { Hub, makeMain } from '../../src';
6+
7+
const PUBLIC_DSN = 'https://username@domain/123';
8+
9+
describe('Sentry.initVueApp', () => {
10+
let loggerWarnings: unknown[] = [];
11+
let warnings: unknown[] = [];
12+
13+
beforeEach(() => {
14+
warnings = [];
15+
loggerWarnings = [];
16+
17+
jest.spyOn(logger, 'warn').mockImplementation((message: unknown) => {
18+
loggerWarnings.push(message);
19+
});
20+
21+
jest.spyOn(console, 'warn').mockImplementation((message: unknown) => {
22+
warnings.push(message);
23+
});
24+
});
25+
26+
afterEach(() => {
27+
jest.resetAllMocks();
28+
});
29+
30+
it('warns when called before SDK.init()', () => {
31+
const hub = new Hub();
32+
makeMain(hub);
33+
34+
const app = createApp({
35+
template: '<div>hello</div>',
36+
});
37+
38+
Sentry.initVueApp(app);
39+
40+
expect(loggerWarnings).toEqual([
41+
'[@sentry/vue]: Cannot initialize as no Client available. Make sure to call `Sentry.init` before calling `initVueApp()`.',
42+
]);
43+
expect(warnings).toEqual([]);
44+
});
45+
46+
it('warns when mounting before SDK.initVueApp()', () => {
47+
Sentry.init({ dsn: PUBLIC_DSN, app: false, autoSessionTracking: false });
48+
49+
const el = document.createElement('div');
50+
const app = createApp({
51+
template: '<div>hello</div>',
52+
});
53+
54+
app.mount(el);
55+
56+
Sentry.initVueApp(app);
57+
58+
expect(warnings).toEqual([
59+
'[@sentry/vue]: Misconfigured SDK. Vue app is already mounted. Make sure to call `app.mount()` after `Sentry.init()`.',
60+
]);
61+
expect(loggerWarnings).toEqual([]);
62+
});
63+
64+
it('works when calling SDK.initVueApp()', () => {
65+
Sentry.init({ dsn: PUBLIC_DSN, app: false, autoSessionTracking: false });
66+
67+
const el = document.createElement('div');
68+
const app = createApp({
69+
template: '<div>hello</div>',
70+
});
71+
72+
Sentry.initVueApp(app);
73+
74+
app.mount(el);
75+
76+
expect(warnings).toEqual([]);
77+
expect(loggerWarnings).toEqual([]);
78+
79+
expect(app.config.errorHandler).toBeDefined();
80+
});
81+
});

0 commit comments

Comments
 (0)