pauseTracking,
resetTracking,
} from '@vue/reactivity'
-import {
- type Component,
- type ComponentInternalInstance,
- SetupContext,
-} from './component'
+import type { Component } from './component'
import { NO, camelize, hasOwn, isFunction } from '@vue/shared'
import { type SchedulerJob, queueJob } from '../../runtime-core/src/scheduler'
import { insert } from './dom/element'
import { normalizeContainer } from './apiRender'
import { normalizePropsOptions, resolvePropValue } from './componentProps'
import type { Block } from './block'
+import { EmitFn, type EmitsOptions } from './componentEmits'
+import { StaticSlots } from './componentSlots'
+import { setDynamicProp } from './dom/prop'
interface RawProps {
[key: string]: PropSource
export function createComponentSimple(
component: Component,
rawProps?: RawProps,
-): Block {
- const instance = new ComponentInstance(
- component,
- rawProps,
- ) as any as ComponentInternalInstance
+ isSingleRoot?: boolean,
+): ComponentInstance {
+ // check if we are the single root of the parent
+ // if yes, inject parent attrs as dynamic props source
+ if (isSingleRoot && currentInstance && currentInstance.hasFallthrough) {
+ if (rawProps) {
+ ;(rawProps.$ || (rawProps.$ = [])).push(currentInstance.attrs)
+ } else {
+ rawProps = { $: [currentInstance.attrs] }
+ }
+ }
+
+ const instance = new ComponentInstance(component, rawProps)
pauseTracking()
let prevInstance = currentInstance
const setupFn = isFunction(component) ? component : component.setup
const setupContext = setupFn!.length > 1 ? new SetupContext(instance) : null
- const node = setupFn!(
+ instance.block = setupFn!(
instance.props,
// @ts-expect-error
setupContext,
- ) as Block
+ ) as Block // TODO handle return object
// single root, inherit attrs
if (
- rawProps &&
+ instance.hasFallthrough &&
component.inheritAttrs !== false &&
- node instanceof Element &&
+ instance.block instanceof Element &&
Object.keys(instance.attrs).length
) {
renderEffectSimple(() => {
- // TODO
+ for (const key in instance.attrs) {
+ setDynamicProp(instance.block as Element, key, instance.attrs[key])
+ }
})
}
instance.scope.off()
currentInstance = prevInstance
resetTracking()
- // @ts-expect-error
- node.__vue__ = instance
- return node
+ return instance
+}
+
+class SetupContext<E = EmitsOptions> {
+ attrs: Record<string, any>
+ // emit: EmitFn<E>
+ // slots: Readonly<StaticSlots>
+ expose: (exposed?: Record<string, any>) => void
+
+ constructor(instance: ComponentInstance) {
+ this.attrs = instance.attrs
+ // this.emit = instance.emit as EmitFn<E>
+ // this.slots = instance.slots
+ this.expose = (exposed = {}) => {
+ instance.exposed = exposed
+ }
+ }
}
let uid = 0
scope: EffectScope = new EffectScope(true)
props: Record<string, any>
attrs: Record<string, any>
+ block: Block
+ exposed?: Record<string, any>
+ hasFallthrough: boolean
+
constructor(comp: Component, rawProps?: RawProps) {
this.type = comp
+ this.block = null! // to be set
// init props
- let mayHaveFallthroughAttrs = false
+ this.hasFallthrough = false
if (comp.props && rawProps && rawProps.$) {
// has dynamic props, use proxy
const handlers = getDynamicPropsHandlers(comp, this)
this.props = new Proxy(rawProps, handlers[0])
this.attrs = new Proxy(rawProps, handlers[1])
- mayHaveFallthroughAttrs = true
+ this.hasFallthrough = true
} else {
- mayHaveFallthroughAttrs = initStaticProps(
+ this.hasFallthrough = initStaticProps(
comp,
rawProps,
(this.props = {}),
}
// TODO validate props
-
- if (mayHaveFallthroughAttrs) {
- // TODO apply fallthrough attrs
- }
// TODO init slots
}
}
+export function isVaporComponent(value: unknown): value is ComponentInstance {
+ return value instanceof ComponentInstance
+}
+
function initStaticProps(
comp: Component,
rawProps: RawProps | undefined,
return castProp(resolveSource(target[key as string]))
}
if (target.$) {
- let source, resolved
- for (source of target.$) {
- resolved = resolveSource(source)
- if (hasOwn(resolved, key)) {
- return castProp(resolved[key])
+ let i = target.$.length
+ let source
+ while (i--) {
+ source = resolveSource(target.$[i])
+ if (hasOwn(source, key)) {
+ return castProp(source[key])
}
}
}
if (key === '$' || isProp(key)) return false
if (hasOwn(target, key)) return true
if (target.$) {
- let source, resolved
- for (source of target.$) {
- resolved = resolveSource(source)
- if (hasOwn(resolved, key)) {
+ let i = target.$.length
+ while (i--) {
+ if (hasOwn(resolveSource(target.$[i]), key)) {
return true
}
}
key => key !== '$' && !isProp(key),
)
if (target.$) {
- for (const source of target.$) {
- staticKeys.push(...Object.keys(resolveSource(source)))
+ let i = target.$.length
+ while (i--) {
+ staticKeys.push(...Object.keys(resolveSource(target.$[i])))
}
}
return staticKeys