]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: make core warning and errorHandling vdom/vapor generic
authorEvan You <evan@vuejs.org>
Tue, 3 Dec 2024 13:43:18 +0000 (21:43 +0800)
committerEvan You <evan@vuejs.org>
Tue, 3 Dec 2024 13:43:18 +0000 (21:43 +0800)
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/errorHandling.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/scheduler.ts
packages/runtime-core/src/warning.ts
packages/runtime-vapor/src/_new/apiCreateApp.ts
packages/runtime-vapor/src/_new/component.ts
packages/runtime-vapor/src/_new/componentEmits.ts
packages/runtime-vapor/src/_new/componentProps.ts
packages/runtime-vapor/src/block.ts

index c877ebd4b900136052b1b902a6e2a8a226dc2f6e..ca6419dce3ee8af218e360d595a755658741f60c 100644 (file)
@@ -235,6 +235,17 @@ export interface ClassComponent {
   __vccOpts: ComponentOptions
 }
 
+/**
+ * Type used where a function accepts both vdom and vapor components.
+ */
+export type GenericComponent = (
+  | {
+      name?: string
+    }
+  | ((() => any) & { displayName?: string })
+) &
+  ComponentInternalOptions
+
 /**
  * Concrete component type matches its actual value: it's either an options
  * object, or a function. Use this where the code expects to work with actual
@@ -308,11 +319,66 @@ export type InternalRenderFunction = {
   _compatWrapped?: boolean // is wrapped for v2 compat
 }
 
+/**
+ * Base component instance interface that is shared between vdom mode and vapor
+ * mode, so that we can have a mixed instance tree and reuse core logic that
+ * operate on both.
+ */
+export interface GenericComponentInstance {
+  uid: number
+  type: GenericComponent
+  parent: GenericComponentInstance | null
+  appContext: AppContext
+  /**
+   * Object containing values this component provides for its descendants
+   * @internal
+   */
+  provides: Data
+  /**
+   * Tracking reactive effects (e.g. watchers) associated with this component
+   * so that they can be automatically stopped on component unmount
+   * @internal
+   */
+  scope: EffectScope
+  /**
+   * SSR render function
+   * (they are the same between vdom and vapor components.)
+   * @internal
+   */
+  ssrRender?: Function | null
+
+  // state
+  props: Data
+  attrs: Data
+  /**
+   * @internal
+   */
+  refs: Data
+  /**
+   * used for keeping track of .once event handlers on components
+   * @internal
+   */
+  emitted: Record<string, boolean> | null
+  /**
+   * used for caching the value returned from props default factory functions to
+   * avoid unnecessary watcher trigger
+   * @internal
+   */
+  propsDefaults: Data | null
+
+  // the following are for error handling logic only
+  proxy?: any
+  /**
+   * @internal
+   */
+  [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
+}
+
 /**
  * We expose a subset of properties on the internal instance as they are
  * useful for advanced external libraries and tools.
  */
-export interface ComponentInternalInstance {
+export interface ComponentInternalInstance extends GenericComponentInstance {
   uid: number
   type: ConcreteComponent
   parent: ComponentInternalInstance | null
@@ -348,16 +414,6 @@ export interface ComponentInternalInstance {
    * @internal
    */
   render: InternalRenderFunction | null
-  /**
-   * SSR render function
-   * @internal
-   */
-  ssrRender?: Function | null
-  /**
-   * Object containing values this component provides for its descendants
-   * @internal
-   */
-  provides: Data
   /**
    * for tracking useId()
    * first element is the current boundary prefix
@@ -365,12 +421,6 @@ export interface ComponentInternalInstance {
    * @internal
    */
   ids: [string, number, number]
-  /**
-   * Tracking reactive effects (e.g. watchers) associated with this component
-   * so that they can be automatically stopped on component unmount
-   * @internal
-   */
-  scope: EffectScope
   /**
    * cache for proxy access type to avoid hasOwnProperty calls
    * @internal
@@ -430,10 +480,27 @@ export interface ComponentInternalInstance {
   ceReload?: (newStyles?: string[]) => void
 
   // the rest are only for stateful components ---------------------------------
+  /**
+   * @internal
+   */
+  setupContext: SetupContext | null
+  /**
+   * setup related
+   * @internal
+   */
+  setupState: Data | null
+  /**
+   * devtools access to additional info
+   * @internal
+   */
+  devtoolsRawSetupState?: any
 
   // main proxy that serves as the public instance (`this`)
   proxy: ComponentPublicInstance | null
 
+  data: Data // options API only
+  emit: EmitFn
+  slots: InternalSlots
   // exposed properties via expose()
   exposed: Record<string, any> | null
   exposeProxy: Record<string, any> | null
@@ -451,41 +518,6 @@ export interface ComponentInternalInstance {
    * @internal
    */
   ctx: Data
-
-  // state
-  data: Data
-  props: Data
-  attrs: Data
-  slots: InternalSlots
-  refs: Data
-  emit: EmitFn
-
-  /**
-   * used for keeping track of .once event handlers on components
-   * @internal
-   */
-  emitted: Record<string, boolean> | null
-  /**
-   * used for caching the value returned from props default factory functions to
-   * avoid unnecessary watcher trigger
-   * @internal
-   */
-  propsDefaults: Data | null
-  /**
-   * setup related
-   * @internal
-   */
-  setupState: Data
-  /**
-   * devtools access to additional info
-   * @internal
-   */
-  devtoolsRawSetupState?: any
-  /**
-   * @internal
-   */
-  setupContext: SetupContext | null
-
   /**
    * suspense related
    * @internal
@@ -600,6 +632,13 @@ const emptyAppContext = createAppContext()
 
 let uid = 0
 
+/**
+ * @internal for vapor
+ */
+export function nextUid(): number {
+  return uid++
+}
+
 export function createComponentInstance(
   vnode: VNode,
   parent: ComponentInternalInstance | null,
@@ -1202,7 +1241,7 @@ const classify = (str: string): string =>
   str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
 
 export function getComponentName(
-  Component: ConcreteComponent,
+  Component: GenericComponent,
   includeInferred = true,
 ): string | false | undefined {
   return isFunction(Component)
@@ -1211,8 +1250,8 @@ export function getComponentName(
 }
 
 export function formatComponentName(
-  instance: ComponentInternalInstance | null,
-  Component: ConcreteComponent,
+  instance: GenericComponentInstance | null,
+  Component: GenericComponent,
   isRoot = false,
 ): string {
   let name = getComponentName(Component)
@@ -1234,7 +1273,7 @@ export function formatComponentName(
     }
     name =
       inferFromRegistry(
-        instance.components ||
+        (instance as ComponentInternalInstance).components ||
           (instance.parent.type as ComponentOptions).components,
       ) || inferFromRegistry(instance.appContext.components)
   }
index 85619244a92620b7451449f3d2cb4a662f04e705..fde2a57c452df8f31487d5c81d494d8423198669 100644 (file)
@@ -30,6 +30,7 @@ import {
   type ComponentInternalInstance,
   type ComponentOptions,
   type ConcreteComponent,
+  type GenericComponentInstance,
   setCurrentInstance,
 } from './component'
 import { isEmitListener } from './componentEmits'
@@ -183,9 +184,15 @@ type NormalizedProp = PropOptions & {
   [BooleanFlags.shouldCastTrue]?: boolean
 }
 
-// normalized value is a tuple of the actual normalized options
-// and an array of prop keys that need value casting (booleans and defaults)
+/**
+ * normalized value is a tuple of the actual normalized options
+ * and an array of prop keys that need value casting (booleans and defaults)
+ * @internal
+ */
 export type NormalizedProps = Record<string, NormalizedProp>
+/**
+ * @internal
+ */
 export type NormalizedPropsOptions = [NormalizedProps, string[]] | []
 
 export function initProps(
@@ -444,18 +451,12 @@ function setFullProps(
   return hasAttrsChanged
 }
 
-/**
- * A type that allows both vdom and vapor instances
- */
-type CommonInstance = Pick<
-  ComponentInternalInstance,
-  'props' | 'propsDefaults' | 'ce'
->
-
 /**
  * @internal for runtime-vapor
  */
-export function resolvePropValue<T extends CommonInstance>(
+export function resolvePropValue<
+  T extends GenericComponentInstance & Pick<ComponentInternalInstance, 'ce'>,
+>(
   options: NormalizedProps,
   key: string,
   value: unknown,
index c4bdf0baccd6a15bbb20658e465cdb3021d9387f..f8048c5c0e7d32000c5e086479055d7a258d2d6b 100644 (file)
@@ -1,6 +1,5 @@
 import { pauseTracking, resetTracking } from '@vue/reactivity'
-import type { VNode } from './vnode'
-import type { ComponentInternalInstance } from './component'
+import type { GenericComponentInstance } from './component'
 import { popWarningContext, pushWarningContext, warn } from './warning'
 import { EMPTY_OBJ, isArray, isFunction, isPromise } from '@vue/shared'
 import { LifecycleHooks } from './enums'
@@ -69,7 +68,7 @@ export type ErrorTypes = LifecycleHooks | ErrorCodes | WatchErrorCodes
 
 export function callWithErrorHandling(
   fn: Function,
-  instance: ComponentInternalInstance | null | undefined,
+  instance: GenericComponentInstance | null | undefined,
   type: ErrorTypes,
   args?: unknown[],
 ): any {
@@ -82,7 +81,7 @@ export function callWithErrorHandling(
 
 export function callWithAsyncErrorHandling(
   fn: Function | Function[],
-  instance: ComponentInternalInstance | null,
+  instance: GenericComponentInstance | null,
   type: ErrorTypes,
   args?: unknown[],
 ): any {
@@ -111,17 +110,16 @@ export function callWithAsyncErrorHandling(
 
 export function handleError(
   err: unknown,
-  instance: ComponentInternalInstance | null | undefined,
+  instance: GenericComponentInstance | null | undefined,
   type: ErrorTypes,
   throwInDev = true,
 ): void {
-  const contextVNode = instance ? instance.vnode : null
   const { errorHandler, throwUnhandledErrorInProduction } =
     (instance && instance.appContext.config) || EMPTY_OBJ
   if (instance) {
     let cur = instance.parent
     // the exposed instance is the render proxy to keep it consistent with 2.x
-    const exposedInstance = instance.proxy
+    const exposedInstance = instance.proxy || instance
     // in production the hook receives only the error code
     const errorInfo = __DEV__
       ? ErrorTypeStrings[type]
@@ -151,23 +149,23 @@ export function handleError(
       return
     }
   }
-  logError(err, type, contextVNode, throwInDev, throwUnhandledErrorInProduction)
+  logError(err, type, instance, throwInDev, throwUnhandledErrorInProduction)
 }
 
 function logError(
   err: unknown,
   type: ErrorTypes,
-  contextVNode: VNode | null,
+  instance: GenericComponentInstance | null | undefined,
   throwInDev = true,
   throwInProd = false,
 ) {
   if (__DEV__) {
     const info = ErrorTypeStrings[type]
-    if (contextVNode) {
-      pushWarningContext(contextVNode)
+    if (instance) {
+      pushWarningContext(instance)
     }
     warn(`Unhandled error${info ? ` during execution of ${info}` : ``}`)
-    if (contextVNode) {
+    if (instance) {
       popWarningContext()
     }
     // crash in dev by default so it's more noticeable
index 9ff31e6d456774cab24c3ca868386464af51c7ba..fc3ade2730a0a8b2a2fdecd1a23d411db47d34da 100644 (file)
@@ -320,7 +320,6 @@ export type {
   ExtractPropTypes,
   ExtractPublicPropTypes,
   ExtractDefaultPropTypes,
-  NormalizedPropsOptions,
 } from './componentProps'
 export type {
   Directive,
@@ -487,6 +486,16 @@ export const DeprecationTypes = (
 // **IMPORTANT** These APIs are exposed solely for @vue/runtime-vapor and may
 // change without notice between versions. User code should never rely on them.
 
-export { baseNormalizePropsOptions, resolvePropValue } from './componentProps'
+export {
+  type NormalizedPropsOptions,
+  baseNormalizePropsOptions,
+  resolvePropValue,
+} from './componentProps'
 export { isEmitListener } from './componentEmits'
 export { type SchedulerJob, queueJob } from './scheduler'
+export {
+  type ComponentInternalOptions,
+  type GenericComponentInstance,
+  type LifecycleHook,
+  nextUid,
+} from './component'
index 5139665b99e23362ab25f518f92e0c8580dd9e2d..8c945d5f6bbc404bb11648390a031285c46a4dd3 100644 (file)
@@ -1,6 +1,6 @@
 import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
 import { NOOP, isArray } from '@vue/shared'
-import { type ComponentInternalInstance, getComponentName } from './component'
+import { type GenericComponentInstance, getComponentName } from './component'
 
 export enum SchedulerJobFlags {
   QUEUED = 1 << 0,
@@ -38,7 +38,7 @@ export interface SchedulerJob extends Function {
    * Attached by renderer.ts when setting up a component's render effect
    * Used to obtain component information when reporting max recursive updates.
    */
-  i?: ComponentInternalInstance
+  i?: GenericComponentInstance
 }
 
 export type SchedulerJobs = SchedulerJob | SchedulerJob[]
@@ -141,7 +141,7 @@ export function queuePostFlushCb(cb: SchedulerJobs): void {
 }
 
 export function flushPreFlushCbs(
-  instance?: ComponentInternalInstance,
+  instance?: GenericComponentInstance,
   seen?: CountMap,
   // skip the current job
   i: number = flushIndex + 1,
index 0ba7caa350e5f972376393652a32408d75d067c7..5ff5f47c6ae428904395a01d99576462a591ea30 100644 (file)
@@ -1,29 +1,27 @@
-import type { VNode } from './vnode'
 import {
   type ComponentInternalInstance,
-  type ConcreteComponent,
+  type GenericComponentInstance,
   formatComponentName,
 } from './component'
 import { isFunction, isString } from '@vue/shared'
 import type { Data } from '@vue/runtime-shared'
 import { isRef, pauseTracking, resetTracking, toRaw } from '@vue/reactivity'
 import { ErrorCodes, callWithErrorHandling } from './errorHandling'
+import { type VNode, isVNode } from './vnode'
 
-type ComponentVNode = VNode & {
-  type: ConcreteComponent
-}
-
-const stack: VNode[] = []
+const stack: (GenericComponentInstance | VNode)[] = []
 
 type TraceEntry = {
-  vnode: ComponentVNode
+  ctx: GenericComponentInstance | VNode
   recurseCount: number
 }
 
 type ComponentTraceStack = TraceEntry[]
 
-export function pushWarningContext(vnode: VNode): void {
-  stack.push(vnode)
+export function pushWarningContext(
+  ctx: GenericComponentInstance | VNode,
+): void {
+  stack.push(ctx)
 }
 
 export function popWarningContext(): void {
@@ -40,7 +38,8 @@ export function warn(msg: string, ...args: any[]): void {
   // during patch, leading to infinite recursion.
   pauseTracking()
 
-  const instance = stack.length ? stack[stack.length - 1].component : null
+  const entry = stack.length ? stack[stack.length - 1] : null
+  const instance = isVNode(entry) ? entry.component : entry
   const appWarnHandler = instance && instance.appContext.config.warnHandler
   const trace = getComponentTrace()
 
@@ -55,7 +54,8 @@ export function warn(msg: string, ...args: any[]): void {
         instance && instance.proxy,
         trace
           .map(
-            ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`,
+            ({ ctx }) =>
+              `at <${formatComponentName(instance, (ctx as any).type)}>`,
           )
           .join('\n'),
         trace,
@@ -79,8 +79,8 @@ export function warn(msg: string, ...args: any[]): void {
 }
 
 export function getComponentTrace(): ComponentTraceStack {
-  let currentVNode: VNode | null = stack[stack.length - 1]
-  if (!currentVNode) {
+  let currentCtx: TraceEntry['ctx'] | null = stack[stack.length - 1]
+  if (!currentCtx) {
     return []
   }
 
@@ -89,19 +89,23 @@ export function getComponentTrace(): ComponentTraceStack {
   // instance parent pointers.
   const normalizedStack: ComponentTraceStack = []
 
-  while (currentVNode) {
+  while (currentCtx) {
     const last = normalizedStack[0]
-    if (last && last.vnode === currentVNode) {
+    if (last && last.ctx === currentCtx) {
       last.recurseCount++
     } else {
       normalizedStack.push({
-        vnode: currentVNode as ComponentVNode,
+        ctx: currentCtx,
         recurseCount: 0,
       })
     }
-    const parentInstance: ComponentInternalInstance | null =
-      currentVNode.component && currentVNode.component.parent
-    currentVNode = parentInstance && parentInstance.vnode
+    if (isVNode(currentCtx)) {
+      const parent: ComponentInternalInstance | null =
+        currentCtx.component && currentCtx.component.parent
+      currentCtx = parent && parent.vnode
+    } else {
+      currentCtx = currentCtx.parent
+    }
   }
 
   return normalizedStack
@@ -116,19 +120,14 @@ function formatTrace(trace: ComponentTraceStack): any[] {
   return logs
 }
 
-function formatTraceEntry({ vnode, recurseCount }: TraceEntry): any[] {
+function formatTraceEntry({ ctx, recurseCount }: TraceEntry): any[] {
   const postfix =
     recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``
-  const isRoot = vnode.component ? vnode.component.parent == null : false
-  const open = ` at <${formatComponentName(
-    vnode.component,
-    vnode.type,
-    isRoot,
-  )}`
+  const instance = isVNode(ctx) ? ctx.component : ctx
+  const isRoot = instance ? instance.parent == null : false
+  const open = ` at <${formatComponentName(instance, (ctx as any).type, isRoot)}`
   const close = `>` + postfix
-  return vnode.props
-    ? [open, ...formatProps(vnode.props), close]
-    : [open + close]
+  return ctx.props ? [open, ...formatProps(ctx.props), close] : [open + close]
 }
 
 function formatProps(props: Data): any[] {
index f6a7c00bcdc10389aa4f8a9e9da22f1c92e1af7e..ffdff0731d76cfb9ddc2c357f6ca819e6634e4cf 100644 (file)
@@ -1,8 +1,8 @@
 import { normalizeContainer } from '../apiRender'
 import { insert } from '../dom/element'
-import { type Component, createComponent } from './component'
+import { type VaporComponent, createComponent } from './component'
 
-export function createVaporApp(comp: Component): any {
+export function createVaporApp(comp: VaporComponent): any {
   return {
     mount(container: string | ParentNode) {
       container = normalizeContainer(container)
index e3281f2fd4b1a6dfc06ab9b06a37098d53524da9..955caf75ac95ecf10a7695c635e1d1d888ace849 100644 (file)
@@ -1,14 +1,19 @@
 import {
+  type AppContext,
+  type ComponentInternalOptions,
   type ComponentPropsOptions,
   EffectScope,
   type EmitsOptions,
+  type GenericComponentInstance,
+  type LifecycleHook,
   type NormalizedPropsOptions,
   type ObjectEmitsOptions,
+  nextUid,
 } from '@vue/runtime-core'
 import type { Block } from '../block'
 import type { Data } from '@vue/runtime-shared'
 import { pauseTracking, resetTracking } from '@vue/reactivity'
-import { isFunction } from '@vue/shared'
+import { EMPTY_OBJ, isFunction } from '@vue/shared'
 import {
   type RawProps,
   getDynamicPropsHandlers,
@@ -17,22 +22,22 @@ import {
 import { setDynamicProp } from '../dom/prop'
 import { renderEffect } from './renderEffect'
 
-export type Component = FunctionalComponent | ObjectComponent
+export type VaporComponent = FunctionalVaporComponent | ObjectVaporComponent
 
-export type SetupFn = (
+export type VaporSetupFn = (
   props: any,
   ctx: SetupContext,
 ) => Block | Data | undefined
 
-export type FunctionalComponent = SetupFn &
-  Omit<ObjectComponent, 'setup'> & {
+export type FunctionalVaporComponent = VaporSetupFn &
+  Omit<ObjectVaporComponent, 'setup'> & {
     displayName?: string
   } & SharedInternalOptions
 
-export interface ObjectComponent
+export interface ObjectVaporComponent
   extends ComponentInternalOptions,
     SharedInternalOptions {
-  setup?: SetupFn
+  setup?: VaporSetupFn
   inheritAttrs?: boolean
   props?: ComponentPropsOptions
   emits?: EmitsOptions
@@ -43,41 +48,27 @@ export interface ObjectComponent
 }
 
 interface SharedInternalOptions {
-  __propsOptions?: NormalizedPropsOptions
-  __propsHandlers?: [ProxyHandler<any>, ProxyHandler<any>]
-  __emitsOptions?: ObjectEmitsOptions
-}
-
-// Note: can't mark this whole interface internal because some public interfaces
-// extend it.
-interface ComponentInternalOptions {
-  /**
-   * @internal
-   */
-  __scopeId?: string
-  /**
-   * @internal
-   */
-  __cssModules?: Data
   /**
-   * @internal
+   * Cached normalized props options.
+   * In vapor mode there are no mixins so normalized options can be cached
+   * directly on the component
    */
-  __hmrId?: string
+  __propsOptions?: NormalizedPropsOptions
   /**
-   * This one should be exposed so that devtools can make use of it
+   * Cached normalized props proxy handlers.
    */
-  __file?: string
+  __propsHandlers?: [ProxyHandler<any>, ProxyHandler<any>]
   /**
-   * name inferred from filename
+   * Cached normalized emits options.
    */
-  __name?: string
+  __emitsOptions?: ObjectEmitsOptions
 }
 
 export function createComponent(
-  component: Component,
+  component: VaporComponent,
   rawProps?: RawProps,
   isSingleRoot?: boolean,
-): ComponentInstance {
+): VaporComponentInstance {
   // check if we are the single root of the parent
   // if yes, inject parent attrs as dynamic props source
   if (isSingleRoot && currentInstance && currentInstance.hasFallthrough) {
@@ -88,7 +79,7 @@ export function createComponent(
     }
   }
 
-  const instance = new ComponentInstance(component, rawProps)
+  const instance = new VaporComponentInstance(component, rawProps)
 
   pauseTracking()
   let prevInstance = currentInstance
@@ -123,23 +114,45 @@ export function createComponent(
   return instance
 }
 
-let uid = 0
-export let currentInstance: ComponentInstance | null = null
+export let currentInstance: VaporComponentInstance | null = null
 
-export class ComponentInstance {
-  type: Component
-  uid: number = uid++
-  scope: EffectScope = new EffectScope(true)
+export class VaporComponentInstance implements GenericComponentInstance {
+  uid: number
+  type: VaporComponent
+  parent: GenericComponentInstance | null
+  appContext: AppContext
+
+  block: Block
+  scope: EffectScope
   props: Record<string, any>
-  propsDefaults: Record<string, any> | null
   attrs: Record<string, any>
-  block: Block
   exposed?: Record<string, any>
+
+  emitted: Record<string, boolean> | null
+  propsDefaults: Record<string, any> | null
+
+  // for useTemplateRef()
+  refs: Data
+  // for provide / inject
+  provides: Data
+
   hasFallthrough: boolean
 
-  constructor(comp: Component, rawProps?: RawProps) {
+  // LifecycleHooks.ERROR_CAPTURED
+  ec: LifecycleHook
+
+  constructor(comp: VaporComponent, rawProps?: RawProps) {
+    this.uid = nextUid()
     this.type = comp
+    this.parent = currentInstance
+    this.appContext = currentInstance ? currentInstance.appContext : null! // TODO
+
     this.block = null! // to be set
+    this.scope = new EffectScope(true)
+
+    this.provides = this.refs = EMPTY_OBJ
+    this.emitted = null
+    this.ec = null
 
     // init props
     this.propsDefaults = null
@@ -161,8 +174,10 @@ export class ComponentInstance {
   }
 }
 
-export function isVaporComponent(value: unknown): value is ComponentInstance {
-  return value instanceof ComponentInstance
+export function isVaporComponent(
+  value: unknown,
+): value is VaporComponentInstance {
+  return value instanceof VaporComponentInstance
 }
 
 export class SetupContext<E = EmitsOptions> {
@@ -171,7 +186,7 @@ export class SetupContext<E = EmitsOptions> {
   // slots: Readonly<StaticSlots>
   expose: (exposed?: Record<string, any>) => void
 
-  constructor(instance: ComponentInstance) {
+  constructor(instance: VaporComponentInstance) {
     this.attrs = instance.attrs
     // this.emit = instance.emit as EmitFn<E>
     // this.slots = instance.slots
index c07c44ce0ec8ac590ac5ed39177f27f9bce9b87e..77c534f733c0cd5a4e9cdc537e512ec191c38128 100644 (file)
@@ -1,12 +1,16 @@
-import type { ObjectEmitsOptions } from '@vue/runtime-core'
-import type { Component } from './component'
-import { isArray } from '@vue/shared'
+import type { EmitFn, ObjectEmitsOptions } from '@vue/runtime-core'
+import {
+  type VaporComponent,
+  type VaporComponentInstance,
+  currentInstance,
+} from './component'
+import { NOOP, isArray } from '@vue/shared'
 
 /**
  * The logic from core isn't too reusable so it's better to duplicate here
  */
 export function normalizeEmitsOptions(
-  comp: Component,
+  comp: VaporComponent,
 ): ObjectEmitsOptions | null {
   const cached = comp.__emitsOptions
   if (cached) return cached
@@ -24,3 +28,20 @@ export function normalizeEmitsOptions(
 
   return (comp.__emitsOptions = normalized)
 }
+
+export function useEmit(): EmitFn {
+  if (!currentInstance) {
+    // TODO warn
+    return NOOP
+  } else {
+    return emit.bind(null, currentInstance)
+  }
+}
+
+export function emit(
+  instance: VaporComponentInstance,
+  event: string,
+  ...rawArgs: any[]
+): void {
+  // TODO extract reusable logic from core
+}
index 105639725a4924ae753ccd5e7f1e260f05c6929c..fb481bb4edffd6aaaac80b0e6ee2b37f0db6440d 100644 (file)
@@ -1,5 +1,5 @@
 import { EMPTY_ARR, NO, camelize, hasOwn, isFunction } from '@vue/shared'
-import type { Component, ComponentInstance } from './component'
+import type { VaporComponent, VaporComponentInstance } from './component'
 import {
   type NormalizedPropsOptions,
   baseNormalizePropsOptions,
@@ -18,9 +18,9 @@ type PropSource<T = any> = T | (() => T)
 type DynamicPropsSource = PropSource<Record<string, any>>
 
 export function initStaticProps(
-  comp: Component,
+  comp: VaporComponent,
   rawProps: RawProps | undefined,
-  instance: ComponentInstance,
+  instance: VaporComponentInstance,
 ): boolean {
   let hasAttrs = false
   const { props, attrs } = instance
@@ -85,7 +85,7 @@ export function initStaticProps(
 
 function resolveDefault(
   factory: (props: Record<string, any>) => unknown,
-  instance: ComponentInstance,
+  instance: VaporComponentInstance,
 ) {
   return factory.call(null, instance.props)
 }
@@ -96,8 +96,8 @@ function resolveSource(source: PropSource): Record<string, any> {
 }
 
 export function getDynamicPropsHandlers(
-  comp: Component,
-  instance: ComponentInstance,
+  comp: VaporComponent,
+  instance: VaporComponentInstance,
 ): [ProxyHandler<RawProps>, ProxyHandler<RawProps>] {
   if (comp.__propsHandlers) {
     return comp.__propsHandlers
@@ -204,7 +204,7 @@ export function getDynamicPropsHandlers(
   return (comp.__propsHandlers = [propsHandlers, attrsHandlers])
 }
 
-function normalizePropsOptions(comp: Component): NormalizedPropsOptions {
+function normalizePropsOptions(comp: VaporComponent): NormalizedPropsOptions {
   const cached = comp.__propsOptions
   if (cached) return cached
 
index 0a3ce4c4e121585e8479ec7848e08f3a51335896..1d3deb46602c6837dc07dc12ebdffe2162831784 100644 (file)
@@ -1,9 +1,9 @@
 import { isArray } from '@vue/shared'
-import { type ComponentInstance, isVaporComponent } from './_new/component'
+import { type VaporComponentInstance, isVaporComponent } from './_new/component'
 
 export const fragmentKey: unique symbol = Symbol(__DEV__ ? `fragmentKey` : ``)
 
-export type Block = Node | Fragment | ComponentInstance | Block[]
+export type Block = Node | Fragment | VaporComponentInstance | Block[]
 export type Fragment = {
   nodes: Block
   anchor?: Node
@@ -27,7 +27,7 @@ export function normalizeBlock(block: Block): Node[] {
 }
 
 export function findFirstRootElement(
-  instance: ComponentInstance,
+  instance: VaporComponentInstance,
 ): Element | undefined {
   const element = getFirstNode(instance.block)
   return element instanceof Element ? element : undefined