Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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