@@ -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
0 commit comments