Skip to content

Commit 0dacbde

Browse files
authored
Merge pull request #112 from codex-team/vue3
feat(vue): vue 3 addons supported
2 parents 5aecc52 + ea585b1 commit 0dacbde

File tree

5 files changed

+128
-11
lines changed

5 files changed

+128
-11
lines changed

.eslintrc.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
module.exports = {
22
root: true,
33
extends: [ 'codex/ts' ],
4+
ignorePatterns: [
5+
'dist/',
6+
'node_modules/',
7+
'package.json',
8+
'tsconfig.json',
9+
],
410
env: {
511
browser: true,
612
},

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "@hawk.so/javascript",
3-
"version": "3.1.0",
3+
"type": "commonjs",
4+
"version": "3.2.0",
45
"description": "JavaScript errors tracking for Hawk.so",
56
"files": [
67
"dist"

src/catcher.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Socket from './modules/socket';
22
import Sanitizer from './modules/sanitizer';
33
import log from './utils/log';
44
import StackParser from './modules/stackParser';
5-
import type { CatcherMessage, HawkInitialSettings } from '@/types';
5+
import type { CatcherMessage, HawkInitialSettings } from './types';
66
import { VueIntegration } from './integrations/vue';
77
import { id } from './utils/id';
88
import type {
@@ -34,6 +34,11 @@ export default class Catcher {
3434
*/
3535
public readonly version: string = VERSION;
3636

37+
/**
38+
* Vue.js integration instance
39+
*/
40+
public vue: VueIntegration | null = null;
41+
3742
/**
3843
* Catcher Type
3944
*/
@@ -202,7 +207,7 @@ export default class Catcher {
202207
*/
203208
public connectVue(vue): void {
204209
// eslint-disable-next-line no-new
205-
new VueIntegration(vue, (error: Error, addons: VueIntegrationAddons) => {
210+
this.vue = new VueIntegration(vue, (error: Error, addons: VueIntegrationAddons) => {
206211
void this.formatAndSend(error, {
207212
vue: addons,
208213
});

src/integrations/vue.ts

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,24 @@ export class VueIntegration {
4949
}
5050
}
5151

52+
/**
53+
* Extract additional useful information from the Vue app
54+
*
55+
* Can be used outside of this class, for example, by Nuxt integration
56+
*
57+
* @param vm - component instance
58+
* @param info - a Vue-specific error info, e.g. which lifecycle hook the error was found in.
59+
*/
60+
public spoilAddons(vm: { [key: string]: unknown }, info: string): VueIntegrationAddons {
61+
const isVue3 = vm.$ !== undefined;
62+
63+
if (isVue3) {
64+
return this.spoilAddonsFromVue3(vm, info);
65+
} else {
66+
return this.spoilAddonsFromVue2(vm, info);
67+
}
68+
}
69+
5270
/**
5371
* Setups event handlers for Vue.js instance
5472
*/
@@ -76,13 +94,13 @@ export class VueIntegration {
7694
}
7795

7896
/**
79-
* Extract additional useful information from the Vue app
97+
* Extract additional useful information from the Vue 2 app
8098
*
81-
* @param vm - vue VM
82-
* @param info - a Vue-specific error info, e.g. which lifecycle hook the error was found in.
99+
* @param vm - component instance
100+
* @param info - which lifecycle hook the error was found in.
83101
*/
84102
// eslint-disable-next-line @typescript-eslint/no-explicit-any
85-
private spoilAddons(vm: { [key: string]: any }, info: string): VueIntegrationAddons {
103+
private spoilAddonsFromVue2(vm: { [key: string]: any }, info: string): VueIntegrationAddons {
86104
const addons: VueIntegrationAddons = {
87105
lifecycle: info,
88106
component: null,
@@ -130,6 +148,89 @@ export class VueIntegration {
130148
return addons;
131149
}
132150

151+
/**
152+
* Extract additional useful information from the Vue 3 app
153+
*
154+
* @param vm - component instance
155+
* @param info - which lifecycle hook the error was found in.
156+
*/
157+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
158+
private spoilAddonsFromVue3(vm: { [key: string]: any }, info: string): VueIntegrationAddons {
159+
const addons: VueIntegrationAddons = {
160+
lifecycle: this.getRuntimeErrorSourceByCode(info),
161+
component: null,
162+
};
163+
164+
/**
165+
* Extract the component name
166+
*/
167+
if (vm.$options !== undefined) {
168+
addons['component'] = `<${vm.$options.__name || vm.$options.name || vm.$options._componentTag || 'Anonymous'}>`;
169+
}
170+
171+
/**
172+
* Fill props
173+
*/
174+
if (Object.keys(vm.$props).length) {
175+
addons['props'] = vm.$props;
176+
}
177+
178+
return addons;
179+
}
180+
181+
/**
182+
* In production, the error code is a link with reference to doc.
183+
* This method returns the error message by the code extracted from the link
184+
*
185+
* @param code - Error source info (3rd argument of the vue:error hook)
186+
* https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured
187+
*/
188+
private getRuntimeErrorSourceByCode(code: string): string {
189+
if (!code.includes('https://vuejs.org/error-reference/#runtime-')) {
190+
return code;
191+
}
192+
193+
const codeParts = code.split('https://vuejs.org/error-reference/#runtime-');
194+
const errorCode = codeParts[codeParts.length - 1];
195+
196+
const errorCodeMap = new Map([
197+
['0', 'setup function'],
198+
['1', 'render function'],
199+
['2', 'watcher getter'],
200+
['3', 'watcher callback'],
201+
['4', 'watcher cleanup function'],
202+
['5', 'native event handler'],
203+
['6', 'component event handler'],
204+
['7', 'vnode hook'],
205+
['8', 'directive hook'],
206+
['9', 'transition hook'],
207+
['10', 'app errorHandler'],
208+
['11', 'app warnHandler'],
209+
['12', 'ref function'],
210+
['13', 'async component loader'],
211+
['14', 'scheduler flush'],
212+
['15', 'component update'],
213+
['16', 'app unmount cleanup function'],
214+
['sp', 'serverPrefetch hook'],
215+
['bc', 'beforeCreate hook'],
216+
['c', 'created hook'],
217+
['bm', 'beforeMount hook'],
218+
['m', 'mounted hook'],
219+
['bu', 'beforeUpdate hook'],
220+
['u', 'updated'],
221+
['bum', 'beforeUnmount hook'],
222+
['um', 'unmounted hook'],
223+
['a', 'activated hook'],
224+
['da', 'deactivated hook'],
225+
['ec', 'errorCaptured hook'],
226+
['rtc', 'renderTracked hook'],
227+
['rtg', 'renderTriggered hook'],
228+
]);
229+
230+
return errorCodeMap.get(errorCode) || code;
231+
}
232+
233+
133234
/**
134235
* Write error to the console
135236
*
@@ -138,13 +239,15 @@ export class VueIntegration {
138239
* @param component - where error was occurred
139240
*/
140241
private printError(err: Error, info: string, component: string | null): void {
242+
const source = this.getRuntimeErrorSourceByCode(info);
243+
141244
if (component === null) {
142-
console.error(`${info}`, err);
245+
console.error(`${source}`, err);
143246

144247
return;
145248
}
146249

147-
console.error(`${component} @ ${info}`, err);
250+
console.error(`${component} @ ${source}`, err);
148251
}
149252
}
150253

tsconfig.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
"declaration": true,
88
"outDir": "dist",
99
"rootDir": "src",
10-
"moduleResolution": "Bundler",
10+
"module": "NodeNext",
11+
"moduleResolution": "nodenext",
1112
"lib": ["dom", "es2017", "es2018"],
1213
"baseUrl": ".",
1314
"paths": {
1415
"@/types": ["src/types"]
15-
}
16+
},
17+
"allowSyntheticDefaultImports": true
1618
},
1719
"include": [
1820
"src/**/*",

0 commit comments

Comments
 (0)