Skip to content

Commit

Permalink
Merge pull request #5 from codex-team/nuxt-addons
Browse files Browse the repository at this point in the history
feat: nuxt error handlers, collection nuxt addons
  • Loading branch information
neSpecc authored Oct 22, 2024
2 parents f64dd24 + ddda012 commit 6161ef3
Show file tree
Hide file tree
Showing 5 changed files with 432 additions and 23 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hawk.so/nuxt",
"version": "0.0.7",
"version": "1.0.0",
"description": "Hawk error tracker integration to Nuxt app",
"repository": {
"type": "git",
Expand All @@ -25,6 +25,7 @@
"build": "nuxt-module-build prepare && nuxt-module-build build",
"dev": "nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:preview": "nuxi preview playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
"release": "yarn run lint && yarn run test && yarn run prepack && changelogen --release && yarn publish && git push --follow-tags",
"lint": "eslint .",
Expand All @@ -34,7 +35,7 @@
"preinstall": "npx only-allow yarn"
},
"dependencies": {
"@hawk.so/javascript": "^3.0.8",
"@hawk.so/javascript": "^3.1.0",
"@hawk.so/vite-plugin": "^1.0.4",
"@nuxt/kit": "^3.13.2"
},
Expand Down
11 changes: 11 additions & 0 deletions playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
Trigger exception
</button>

<TheButton
text="Trigger exception from component"
@activate="componentEventHandler"
/>

<button @click="manuallyFromComposable">
Manually send from useSomething() composable
</button>
Expand All @@ -15,6 +20,8 @@
</template>

<script setup lang="ts">
import TheButton from '~/components/UiButton.vue'
const something = useSomething()
function triggerException() {
Expand All @@ -24,6 +31,10 @@ function triggerException() {
function manuallyFromComposable() {
something.testManualSendingFromComposable(new Error('Error sent manually from composable'))
}
function componentEventHandler() {
return erererer.bb
}
</script>

<style module>
Expand Down
22 changes: 22 additions & 0 deletions playground/components/UiButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<button
@click="emit('activate')"
>
{{ text }} {{ myComputedProperty }}
</button>
</template>

<script setup lang="ts">
import { defineEmits } from 'vue'
const emit = defineEmits<{
activate: []
stop: []
}>()
const props = defineProps<{
text: string
}>()
const myComputedProperty = computed(() => props.text.length)
</script>
134 changes: 131 additions & 3 deletions src/runtime/plugin.client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import HawkCatcher from '@hawk.so/javascript'
// import HawkCatcher from '@hawk.so/javascript'
import HawkCatcher, { type NuxtIntegrationData, type NuxtIntegrationAddons } from '../../../hawk.javascript'
import type { HawkModuleConfig } from '../types'
import beforeSend from '#build/hawk-before-send.mjs'
import { defineNuxtPlugin, useRuntimeConfig } from '#app'
Expand Down Expand Up @@ -35,20 +36,147 @@ export default defineNuxtPlugin((nuxtApp) => {
token: hawkConfig.token,
vue: nuxtApp.vueApp,
release: getReleaseId(),
disableVueErrorHandler: true,
}, {
beforeSend,
}))

/**
* @todo use NuxtApp to extract useful information:
* - current route
* - is SSR
* - Vue Component
* - SSR Request headers ?
*/

nuxtApp.hook('vue:error', (error: unknown, instance, info) => {
try {
const addons = spoilAddons(nuxtApp, info, instance)

hawkInstance.captureError(error as Error, addons)
}
catch (error) {
console.warn('Hawk nuxt unable to capture error. Report this error to maintainers: https://github.com/codex-team/hawk.nuxt/issues/new', error)
}
})

nuxtApp.hook('app:error', (error) => {
try {
const addons = spoilAddons(nuxtApp, 'App startup error')

hawkInstance.captureError(error as Error, addons)
}
catch (error) {
console.warn('Hawk nuxt unable to capture error. Report this error to maintainers: https://github.com/codex-team/hawk.nuxt/issues/new', error)
}
})

/**
* Add Hawk instance to the global context
*/
nuxtApp.$hawk = hawkInstance
})

/**
* Extracts useful information from the Vue component instance and Nuxt app
* @param nuxtApp - Nuxt app instance
* @param info - Error source info (3rd argument of the vue:error hook)
* @param componentInstance - Vue component public instance
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function spoilAddons(nuxtApp: any, info: string, componentInstance?: any): NuxtIntegrationData {
const addons = {} as NuxtIntegrationAddons

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

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

/**
* Extract the current route
*/
if (nuxtApp.$route) {
addons['Route'] = {
path: nuxtApp.$route.path,
fullPath: nuxtApp.$route.fullPath,
}

if (nuxtApp.$route.name) {
addons['Route'].name = nuxtApp.$route.name
}

if (nuxtApp.$route.redirectedFrom) {
addons['Route'].redirectedFrom = nuxtApp.$route.redirectedFrom
}
}

/**
* Error source type
*/
addons['Source'] = getRuntimeErrorSourceByCode(info)

return {
nuxt: 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
*/
function 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
}
Loading

0 comments on commit 6161ef3

Please sign in to comment.