Skip to content

Commit c4f46e0

Browse files
committed
Make linters happy
1 parent a72bc6a commit c4f46e0

File tree

2 files changed

+93
-73
lines changed

2 files changed

+93
-73
lines changed

packages/integrations/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"module": "esm/index.js",
1717
"types": "dist/index.d.ts",
1818
"dependencies": {
19+
"@sentry/apm": "5.15.5",
1920
"@sentry/types": "5.15.5",
2021
"@sentry/utils": "5.15.5",
2122
"tslib": "^1.9.3"

packages/integrations/src/vue.ts

Lines changed: 92 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
1+
import { Integrations as APMIntegrations, Span as SpanClass } from '@sentry/apm';
12
import { EventProcessor, Hub, Integration } from '@sentry/types';
23
import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils';
3-
import { Integrations as APMIntegrations, Span as SpanClass } from '@sentry/apm';
44

5+
/** Global Vue object limited to the methods/attributes we require */
56
interface VueInstance {
67
config?: {
7-
errorHandler(error: Error, vm?: ViewModel, info?: string): void;
8+
errorHandler?(error: Error, vm?: ViewModel, info?: string): void; // tslint:disable-line:completed-docs
89
};
9-
mixin(opts: { [key: string]: () => void }): void;
10+
mixin(hooks: { [key: string]: () => void }): void; // tslint:disable-line:completed-docs
1011
util: {
11-
warn(...input: any): void;
12+
warn(...input: any): void; // tslint:disable-line:completed-docs
1213
};
1314
}
1415

16+
/** Representation of Vue component internals */
17+
interface ViewModel {
18+
[key: string]: any;
19+
$root: object;
20+
$options: {
21+
[key: string]: any;
22+
name?: string;
23+
propsData?: { [key: string]: any };
24+
_componentTag?: string;
25+
__file?: string;
26+
$_sentryPerfHook?: boolean;
27+
};
28+
$once(hook: string, cb: () => void): void; // tslint:disable-line:completed-docs
29+
}
30+
31+
// tslint:enable:completed-docs
32+
33+
/** Vue Integration configuration */
1534
interface IntegrationOptions {
1635
/** Vue instance to be used inside the integration */
1736
Vue: VueInstance;
@@ -41,30 +60,17 @@ interface TracingOptions {
4160
* Can be either set to `boolean` to enable/disable tracking for all of them.
4261
* Or to an array of specific component names (case-sensitive).
4362
*/
44-
trackComponents: boolean | Array<string>;
63+
trackComponents: boolean | string[];
4564
/** How long to wait until the tracked root activity is marked as finished and sent of to Sentry */
4665
timeout: number;
4766
/**
4867
* List of hooks to keep track of during component lifecycle.
4968
* Available hooks: https://vuejs.org/v2/api/#Options-Lifecycle-Hooks
5069
*/
51-
hooks: Array<Hook>;
52-
}
53-
54-
interface ViewModel {
55-
[key: string]: any;
56-
$root: object;
57-
$options: {
58-
[key: string]: any;
59-
name?: string;
60-
propsData?: { [key: string]: any };
61-
_componentTag?: string;
62-
__file?: string;
63-
$_sentryPerfHook?: boolean;
64-
};
65-
$once: (hook: string, cb: () => void) => void;
70+
hooks: Hook[];
6671
}
6772

73+
/** Optional metadata attached to Sentry Event */
6874
interface Metadata {
6975
[key: string]: any;
7076
componentName?: string;
@@ -74,32 +80,32 @@ interface Metadata {
7480

7581
// https://vuejs.org/v2/api/#Options-Lifecycle-Hooks
7682
type Hook =
83+
| 'activated'
7784
| 'beforeCreate'
78-
| 'created'
85+
| 'beforeDestroy'
7986
| 'beforeMount'
80-
| 'mounted'
8187
| 'beforeUpdate'
82-
| 'updated'
83-
| 'activated'
88+
| 'created'
8489
| 'deactivated'
85-
| 'beforeDestroy'
86-
| 'destroyed';
90+
| 'destroyed'
91+
| 'mounted'
92+
| 'updated';
8793

88-
type Operation = 'create' | 'mount' | 'update' | 'activate' | 'destroy';
94+
type Operation = 'activate' | 'create' | 'destroy' | 'mount' | 'update';
8995

9096
// Mappings from lifecycle hook to corresponding operation,
9197
// used to track already started measurements.
9298
const OPERATIONS: { [key in Hook]: Operation } = {
99+
activated: 'activate',
93100
beforeCreate: 'create',
94-
created: 'create',
101+
beforeDestroy: 'destroy',
95102
beforeMount: 'mount',
96-
mounted: 'mount',
97103
beforeUpdate: 'update',
98-
updated: 'update',
99-
activated: 'activate',
104+
created: 'create',
100105
deactivated: 'activate',
101-
beforeDestroy: 'destroy',
102106
destroyed: 'destroy',
107+
mounted: 'mount',
108+
updated: 'update',
103109
};
104110

105111
const COMPONENT_NAME_REGEXP = /(?:^|[-_/])(\w)/g;
@@ -118,36 +124,39 @@ export class Vue implements Integration {
118124
*/
119125
public static id: string = 'Vue';
120126

121-
private _options: IntegrationOptions;
127+
private readonly _options: IntegrationOptions;
122128

123129
/**
124130
* Cache holding already processed component names
125131
*/
126-
private componentsCache = Object.create(null);
127-
private rootSpan?: SpanClass;
128-
private rootSpanTimer?: ReturnType<typeof setTimeout>;
129-
private tracingActivity?: number;
132+
private readonly _componentsCache: { [key: string]: string } = {};
133+
private _rootSpan?: SpanClass;
134+
private _rootSpanTimer?: ReturnType<typeof setTimeout>;
135+
private _tracingActivity?: number;
130136

131137
/**
132138
* @inheritDoc
133139
*/
134140
public constructor(options: Partial<IntegrationOptions>) {
135141
this._options = {
136-
Vue: getGlobalObject<any>().Vue,
142+
Vue: getGlobalObject<any>().Vue, // tslint:disable-line:no-unsafe-any
137143
attachProps: true,
138144
logErrors: false,
139145
tracing: false,
140146
...options,
141147
tracingOptions: {
142-
trackComponents: false,
143148
hooks: ['beforeMount', 'mounted', 'beforeUpdate', 'updated'],
144149
timeout: 2000,
150+
trackComponents: false,
145151
...options.tracingOptions,
146152
},
147153
};
148154
}
149155

150-
private getComponentName(vm: ViewModel): string {
156+
/**
157+
* Extract component name from the ViewModel
158+
*/
159+
private _getComponentName(vm: ViewModel): string {
151160
// Such level of granularity is most likely not necessary, but better safe than sorry. — Kamil
152161
if (!vm) {
153162
return ANONYMOUS_COMPONENT_NAME;
@@ -174,22 +183,27 @@ export class Vue implements Integration {
174183
const unifiedFile = vm.$options.__file.replace(/^[a-zA-Z]:/, '').replace(/\\/g, '/');
175184
const filename = basename(unifiedFile, '.vue');
176185
return (
177-
this.componentsCache[filename] ||
178-
(this.componentsCache[filename] = filename.replace(COMPONENT_NAME_REGEXP, (_, c) => (c ? c.toUpperCase() : '')))
186+
this._componentsCache[filename] ||
187+
(this._componentsCache[filename] = filename.replace(COMPONENT_NAME_REGEXP, (_, c: string) =>
188+
c ? c.toUpperCase() : '',
189+
))
179190
);
180191
}
181192

182193
return ANONYMOUS_COMPONENT_NAME;
183194
}
184195

185-
private applyTracingHooks(vm: ViewModel, getCurrentHub: () => Hub): void {
196+
/** Keep it as attribute function, to keep correct `this` binding inside the hooks callbacks */
197+
private readonly _applyTracingHooks = (vm: ViewModel, getCurrentHub: () => Hub) => {
186198
// Don't attach twice, just in case
187-
if (vm.$options.$_sentryPerfHook) return;
199+
if (vm.$options.$_sentryPerfHook) {
200+
return;
201+
}
188202
vm.$options.$_sentryPerfHook = true;
189203

190-
const name = this.getComponentName(vm);
204+
const name = this._getComponentName(vm);
191205
const rootMount = name === ROOT_COMPONENT_NAME;
192-
const spans: { [key: string]: any } = {};
206+
const spans: { [key: string]: SpanClass } = {};
193207

194208
// Render hook starts after once event is emitted,
195209
// but it ends before the second event of the same type.
@@ -201,14 +215,14 @@ export class Vue implements Integration {
201215

202216
// On the first handler call (before), it'll be undefined, as `$once` will add it in the future.
203217
// However, on the second call (after), it'll be already in place.
204-
if (this.rootSpan) {
205-
this.finishRootSpan(now);
218+
if (this._rootSpan) {
219+
this._finishRootSpan(now);
206220
} else {
207221
vm.$once(`hook:${hook}`, () => {
208222
// Create an activity on the first event call. There'll be no second call, as rootSpan will be in place,
209223
// thus new event handler won't be attached.
210-
this.tracingActivity = APMIntegrations.Tracing.pushActivity('Vue Application Render');
211-
this.rootSpan = getCurrentHub().startSpan({
224+
this._tracingActivity = APMIntegrations.Tracing.pushActivity('Vue Application Render');
225+
this._rootSpan = getCurrentHub().startSpan({
212226
description: 'Application Render',
213227
op: 'Vue',
214228
}) as SpanClass;
@@ -222,7 +236,7 @@ export class Vue implements Integration {
222236
? this._options.tracingOptions.trackComponents.includes(name)
223237
: this._options.tracingOptions.trackComponents;
224238

225-
if (!this.rootSpan || !shouldTrack) {
239+
if (!this._rootSpan || !shouldTrack) {
226240
return;
227241
}
228242

@@ -234,11 +248,11 @@ export class Vue implements Integration {
234248
// However, on the second call (after), it'll be already in place.
235249
if (span) {
236250
span.finish();
237-
this.finishRootSpan(now);
251+
this._finishRootSpan(now);
238252
} else {
239253
vm.$once(`hook:${hook}`, () => {
240-
if (this.rootSpan) {
241-
spans[op] = this.rootSpan.child({
254+
if (this._rootSpan) {
255+
spans[op] = this._rootSpan.child({
242256
description: `Vue <${name}>`,
243257
op,
244258
});
@@ -260,28 +274,30 @@ export class Vue implements Integration {
260274
vm.$options[hook] = [handler];
261275
}
262276
});
263-
}
277+
};
264278

265-
private finishRootSpan(timestamp: number): void {
266-
if (this.rootSpanTimer) {
267-
clearTimeout(this.rootSpanTimer);
279+
/** Finish top-level span and activity with a debounce configured using `timeout` option */
280+
private _finishRootSpan(timestamp: number): void {
281+
if (this._rootSpanTimer) {
282+
clearTimeout(this._rootSpanTimer);
268283
}
269284

270-
this.rootSpanTimer = setTimeout(() => {
271-
if (this.rootSpan) {
272-
this.rootSpan.timestamp = timestamp;
285+
this._rootSpanTimer = setTimeout(() => {
286+
if (this._rootSpan) {
287+
this._rootSpan.timestamp = timestamp;
273288
}
274-
if (this.tracingActivity) {
275-
APMIntegrations.Tracing.popActivity(this.tracingActivity);
289+
if (this._tracingActivity) {
290+
APMIntegrations.Tracing.popActivity(this._tracingActivity);
276291
}
277292
}, this._options.tracingOptions.timeout);
278293
}
279294

280-
private startTracing(getCurrentHub: () => Hub): void {
281-
const applyTracingHooks = this.applyTracingHooks.bind(this);
295+
/** Inject configured tracing hooks into Vue's component lifecycles */
296+
private _startTracing(getCurrentHub: () => Hub): void {
297+
const applyTracingHooks = this._applyTracingHooks;
282298

283299
this._options.Vue.mixin({
284-
beforeCreate() {
300+
beforeCreate(this: ViewModel): void {
285301
// TODO: Move this check to `setupOnce` when we rework integrations initialization in v6
286302
if (getCurrentHub().getIntegration(APMIntegrations.Tracing)) {
287303
// `this` points to currently rendered component
@@ -293,19 +309,21 @@ export class Vue implements Integration {
293309
});
294310
}
295311

296-
private attachErrorHandler(getCurrentHub: () => Hub): void {
312+
/** Inject Sentry's handler into owns Vue's error handler */
313+
private _attachErrorHandler(getCurrentHub: () => Hub): void {
297314
if (!this._options.Vue.config) {
298-
return logger.error('Vue instance is missing required `config` attribute');
315+
logger.error('Vue instance is missing required `config` attribute');
316+
return;
299317
}
300318

301-
const currentErrorHandler = this._options.Vue.config.errorHandler;
319+
const currentErrorHandler = this._options.Vue.config.errorHandler; // tslint:disable-line:no-unbound-method
302320

303321
this._options.Vue.config.errorHandler = (error: Error, vm?: ViewModel, info?: string): void => {
304322
const metadata: Metadata = {};
305323

306324
if (vm) {
307325
try {
308-
metadata.componentName = this.getComponentName(vm);
326+
metadata.componentName = this._getComponentName(vm);
309327

310328
if (this._options.attachProps) {
311329
metadata.propsData = vm.$options.propsData;
@@ -345,13 +363,14 @@ export class Vue implements Integration {
345363
*/
346364
public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
347365
if (!this._options.Vue) {
348-
return logger.error('Vue integration is missing a Vue instance');
366+
logger.error('Vue integration is missing a Vue instance');
367+
return;
349368
}
350369

351-
this.attachErrorHandler(getCurrentHub);
370+
this._attachErrorHandler(getCurrentHub);
352371

353372
if (this._options.tracing) {
354-
this.startTracing(getCurrentHub);
373+
this._startTracing(getCurrentHub);
355374
}
356375
}
357376
}

0 commit comments

Comments
 (0)