]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-vapor): onErrorCaptured (#71)
authorGaoNeng <31283122+GaoNeng-wWw@users.noreply.github.com>
Sun, 24 Dec 2023 18:44:01 +0000 (02:44 +0800)
committerGitHub <noreply@github.com>
Sun, 24 Dec 2023 18:44:01 +0000 (02:44 +0800)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
packages/runtime-vapor/src/apiLifecycle.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/enums.ts [new file with mode: 0644]
packages/runtime-vapor/src/errorHandling.ts

index eff675728ded3270606292f8490b17307e2929af..5c270d1ca94871ab4d0e2b6ec7972ff551afa43b 100644 (file)
@@ -1,38 +1,53 @@
-import { type ComponentInternalInstance, currentInstance } from './component'
-
-export enum VaporLifecycleHooks {
-  BEFORE_CREATE = 'bc',
-  CREATED = 'c',
-  BEFORE_MOUNT = 'bm',
-  MOUNTED = 'm',
-  BEFORE_UPDATE = 'bu',
-  UPDATED = 'u',
-  BEFORE_UNMOUNT = 'bum',
-  UNMOUNTED = 'um',
-  DEACTIVATED = 'da',
-  ACTIVATED = 'a',
-  RENDER_TRIGGERED = 'rtg',
-  RENDER_TRACKED = 'rtc',
-  ERROR_CAPTURED = 'ec',
-  // SERVER_PREFETCH = 'sp',
-}
+import {
+  type ComponentInternalInstance,
+  currentInstance,
+  setCurrentInstance,
+  unsetCurrentInstance,
+} from './component'
+import { warn } from './warning'
+import { pauseTracking, resetTracking } from '@vue/reactivity'
+import { ErrorTypeStrings, callWithAsyncErrorHandling } from './errorHandling'
+import { toHandlerKey } from '@vue/shared'
+import { VaporLifecycleHooks } from './enums'
 
 export const injectHook = (
   type: VaporLifecycleHooks,
-  hook: Function,
+  hook: Function & { __weh?: Function },
   target: ComponentInternalInstance | null = currentInstance,
   prepend: boolean = false,
 ) => {
   if (target) {
     const hooks = target[type] || (target[type] = [])
+    const wrappedHook =
+      hook.__weh ||
+      (hook.__weh = (...args: unknown[]) => {
+        if (target.isUnmounted) {
+          return
+        }
+        pauseTracking()
+        setCurrentInstance(target)
+        const res = callWithAsyncErrorHandling(hook, target, type, args)
+        unsetCurrentInstance()
+        resetTracking()
+        return res
+      })
     if (prepend) {
-      hooks.unshift(hook)
+      hooks.unshift(wrappedHook)
     } else {
-      hooks.push(hook)
+      hooks.push(wrappedHook)
     }
-    return hook
+    return wrappedHook
   } else if (__DEV__) {
-    // TODO: warn need
+    const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''))
+    warn(
+      `${apiName} is called when there is no active component instance to be ` +
+        `associated with. ` +
+        `Lifecycle injection APIs can only be used during execution of setup().` +
+        (__FEATURE_SUSPENSE__
+          ? ` If you are using async setup(), make sure to register lifecycle ` +
+            `hooks before the first await statement.`
+          : ``),
+    )
   }
 }
 export const createHook =
@@ -46,3 +61,16 @@ export const onBeforeUpdate = createHook(VaporLifecycleHooks.BEFORE_UPDATE)
 export const onUpdated = createHook(VaporLifecycleHooks.UPDATED)
 export const onBeforeUnmount = createHook(VaporLifecycleHooks.BEFORE_UNMOUNT)
 export const onUnmounted = createHook(VaporLifecycleHooks.UNMOUNTED)
+
+export type ErrorCapturedHook<TError = unknown> = (
+  err: TError,
+  instance: ComponentInternalInstance | null,
+  info: string,
+) => boolean | void
+
+export function onErrorCaptured<TError = Error>(
+  hook: ErrorCapturedHook<TError>,
+  target: ComponentInternalInstance | null = currentInstance,
+) {
+  injectHook(VaporLifecycleHooks.ERROR_CAPTURED, hook, target)
+}
index 63e1f25285358974e0c426d73fb69f36e9904b1d..914f7beb6ef51e1c4e7a192595002e707ac4e052 100644 (file)
@@ -10,7 +10,7 @@ import {
 } from './componentProps'
 
 import type { Data } from '@vue/shared'
-import { VaporLifecycleHooks } from './apiLifecycle'
+import { VaporLifecycleHooks } from './enums'
 
 export type Component = FunctionalComponent | ObjectComponent
 
diff --git a/packages/runtime-vapor/src/enums.ts b/packages/runtime-vapor/src/enums.ts
new file mode 100644 (file)
index 0000000..b6714f7
--- /dev/null
@@ -0,0 +1,16 @@
+export enum VaporLifecycleHooks {
+  BEFORE_CREATE = 'bc',
+  CREATED = 'c',
+  BEFORE_MOUNT = 'bm',
+  MOUNTED = 'm',
+  BEFORE_UPDATE = 'bu',
+  UPDATED = 'u',
+  BEFORE_UNMOUNT = 'bum',
+  UNMOUNTED = 'um',
+  DEACTIVATED = 'da',
+  ACTIVATED = 'a',
+  RENDER_TRIGGERED = 'rtg',
+  RENDER_TRACKED = 'rtc',
+  ERROR_CAPTURED = 'ec',
+  // SERVER_PREFETCH = 'sp',
+}
index 7c0056512ae931d5bd33d395d3ee27e2636aa09b..d7449f3b4c41126e7078302540937623d4d5b6b3 100644 (file)
@@ -3,10 +3,10 @@
 // The ultimate aim is to uncouple this replicated code and
 // facilitate its shared use between two runtimes.
 
-import { VaporLifecycleHooks } from './apiLifecycle'
 import { type ComponentInternalInstance } from './component'
 import { isFunction, isPromise } from '@vue/shared'
 import { warn } from './warning'
+import { VaporLifecycleHooks } from './enums'
 
 // contexts where user provided function may be executed, in addition to
 // lifecycle hooks.