mirror of https://github.com/vuejs/core.git
refactor: make core warning and errorHandling vdom/vapor generic
This commit is contained in:
parent
4ea66770be
commit
72d82353ee
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[] {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
/**
|
||||
* Cached normalized props options.
|
||||
* In vapor mode there are no mixins so normalized options can be cached
|
||||
* directly on the component
|
||||
*/
|
||||
__propsOptions?: NormalizedPropsOptions
|
||||
/**
|
||||
* Cached normalized props proxy handlers.
|
||||
*/
|
||||
__propsHandlers?: [ProxyHandler<any>, ProxyHandler<any>]
|
||||
/**
|
||||
* Cached normalized emits options.
|
||||
*/
|
||||
__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
|
||||
*/
|
||||
__hmrId?: string
|
||||
/**
|
||||
* This one should be exposed so that devtools can make use of it
|
||||
*/
|
||||
__file?: string
|
||||
/**
|
||||
* name inferred from filename
|
||||
*/
|
||||
__name?: string
|
||||
}
|
||||
|
||||
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 VaporComponentInstance implements GenericComponentInstance {
|
||||
uid: number
|
||||
type: VaporComponent
|
||||
parent: GenericComponentInstance | null
|
||||
appContext: AppContext
|
||||
|
||||
export class ComponentInstance {
|
||||
type: Component
|
||||
uid: number = uid++
|
||||
scope: EffectScope = new EffectScope(true)
|
||||
props: Record<string, any>
|
||||
propsDefaults: Record<string, any> | null
|
||||
attrs: Record<string, any>
|
||||
block: Block
|
||||
scope: EffectScope
|
||||
props: Record<string, any>
|
||||
attrs: Record<string, any>
|
||||
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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue