Skip to content

Commit

Permalink
Merge pull request #112 from codex-team/vue3
Browse files Browse the repository at this point in the history
feat(vue): vue 3 addons supported
  • Loading branch information
neSpecc authored Nov 13, 2024
2 parents 5aecc52 + ea585b1 commit 0dacbde
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
module.exports = {
root: true,
extends: [ 'codex/ts' ],
ignorePatterns: [
'dist/',
'node_modules/',
'package.json',
'tsconfig.json',
],
env: {
browser: true,
},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@hawk.so/javascript",
"version": "3.1.0",
"type": "commonjs",
"version": "3.2.0",
"description": "JavaScript errors tracking for Hawk.so",
"files": [
"dist"
Expand Down
9 changes: 7 additions & 2 deletions src/catcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Socket from './modules/socket';
import Sanitizer from './modules/sanitizer';
import log from './utils/log';
import StackParser from './modules/stackParser';
import type { CatcherMessage, HawkInitialSettings } from '@/types';
import type { CatcherMessage, HawkInitialSettings } from './types';
import { VueIntegration } from './integrations/vue';
import { id } from './utils/id';
import type {
Expand Down Expand Up @@ -34,6 +34,11 @@ export default class Catcher {
*/
public readonly version: string = VERSION;

/**
* Vue.js integration instance
*/
public vue: VueIntegration | null = null;

/**
* Catcher Type
*/
Expand Down Expand Up @@ -202,7 +207,7 @@ export default class Catcher {
*/
public connectVue(vue): void {
// eslint-disable-next-line no-new
new VueIntegration(vue, (error: Error, addons: VueIntegrationAddons) => {
this.vue = new VueIntegration(vue, (error: Error, addons: VueIntegrationAddons) => {
void this.formatAndSend(error, {
vue: addons,
});
Expand Down
115 changes: 109 additions & 6 deletions src/integrations/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ export class VueIntegration {
}
}

/**
* Extract additional useful information from the Vue app
*
* Can be used outside of this class, for example, by Nuxt integration
*
* @param vm - component instance
* @param info - a Vue-specific error info, e.g. which lifecycle hook the error was found in.
*/
public spoilAddons(vm: { [key: string]: unknown }, info: string): VueIntegrationAddons {
const isVue3 = vm.$ !== undefined;

if (isVue3) {
return this.spoilAddonsFromVue3(vm, info);
} else {
return this.spoilAddonsFromVue2(vm, info);
}
}

/**
* Setups event handlers for Vue.js instance
*/
Expand Down Expand Up @@ -76,13 +94,13 @@ export class VueIntegration {
}

/**
* Extract additional useful information from the Vue app
* Extract additional useful information from the Vue 2 app
*
* @param vm - vue VM
* @param info - a Vue-specific error info, e.g. which lifecycle hook the error was found in.
* @param vm - component instance
* @param info - which lifecycle hook the error was found in.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private spoilAddons(vm: { [key: string]: any }, info: string): VueIntegrationAddons {
private spoilAddonsFromVue2(vm: { [key: string]: any }, info: string): VueIntegrationAddons {
const addons: VueIntegrationAddons = {
lifecycle: info,
component: null,
Expand Down Expand Up @@ -130,6 +148,89 @@ export class VueIntegration {
return addons;
}

/**
* Extract additional useful information from the Vue 3 app
*
* @param vm - component instance
* @param info - which lifecycle hook the error was found in.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private spoilAddonsFromVue3(vm: { [key: string]: any }, info: string): VueIntegrationAddons {
const addons: VueIntegrationAddons = {
lifecycle: this.getRuntimeErrorSourceByCode(info),
component: null,
};

/**
* Extract the component name
*/
if (vm.$options !== undefined) {
addons['component'] = `<${vm.$options.__name || vm.$options.name || vm.$options._componentTag || 'Anonymous'}>`;
}

/**
* Fill props
*/
if (Object.keys(vm.$props).length) {
addons['props'] = vm.$props;
}

return addons;
}

/**
* In production, the error code is a link with reference to doc.
* This method returns the error message by the code extracted from the link
*
* @param code - Error source info (3rd argument of the vue:error hook)
* https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured
*/
private getRuntimeErrorSourceByCode(code: string): string {
if (!code.includes('https://vuejs.org/error-reference/#runtime-')) {
return code;
}

const codeParts = code.split('https://vuejs.org/error-reference/#runtime-');
const errorCode = codeParts[codeParts.length - 1];

const errorCodeMap = new Map([
['0', 'setup function'],
['1', 'render function'],
['2', 'watcher getter'],
['3', 'watcher callback'],
['4', 'watcher cleanup function'],
['5', 'native event handler'],
['6', 'component event handler'],
['7', 'vnode hook'],
['8', 'directive hook'],
['9', 'transition hook'],
['10', 'app errorHandler'],
['11', 'app warnHandler'],
['12', 'ref function'],
['13', 'async component loader'],
['14', 'scheduler flush'],
['15', 'component update'],
['16', 'app unmount cleanup function'],
['sp', 'serverPrefetch hook'],
['bc', 'beforeCreate hook'],
['c', 'created hook'],
['bm', 'beforeMount hook'],
['m', 'mounted hook'],
['bu', 'beforeUpdate hook'],
['u', 'updated'],
['bum', 'beforeUnmount hook'],
['um', 'unmounted hook'],
['a', 'activated hook'],
['da', 'deactivated hook'],
['ec', 'errorCaptured hook'],
['rtc', 'renderTracked hook'],
['rtg', 'renderTriggered hook'],
]);

return errorCodeMap.get(errorCode) || code;
}


/**
* Write error to the console
*
Expand All @@ -138,13 +239,15 @@ export class VueIntegration {
* @param component - where error was occurred
*/
private printError(err: Error, info: string, component: string | null): void {
const source = this.getRuntimeErrorSourceByCode(info);

if (component === null) {
console.error(`${info}`, err);
console.error(`${source}`, err);

return;
}

console.error(`${component} @ ${info}`, err);
console.error(`${component} @ ${source}`, err);
}
}

6 changes: 4 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"moduleResolution": "Bundler",
"module": "NodeNext",
"moduleResolution": "nodenext",
"lib": ["dom", "es2017", "es2018"],
"baseUrl": ".",
"paths": {
"@/types": ["src/types"]
}
},
"allowSyntheticDefaultImports": true
},
"include": [
"src/**/*",
Expand Down

0 comments on commit 0dacbde

Please sign in to comment.