})
})
- describe.todo('emit', () => {})
+ describe('emit', () => {
+ test('emit from vapor child to vdom parent', () => {
+ const VaporChild = defineVaporComponent({
+ emits: ['click'],
+ setup(_, { emit }) {
+ emit('click')
+ return []
+ },
+ })
+
+ const fn = vi.fn()
+ define({
+ setup() {
+ return () => h(VaporChild as any, { onClick: fn })
+ },
+ }).render()
+
+ // fn should be called once
+ expect(fn).toHaveBeenCalledTimes(1)
+ })
+ })
describe('slots', () => {
test('basic', () => {
import { type ObjectEmitsOptions, baseEmit } from '@vue/runtime-dom'
import type { VaporComponent, VaporComponentInstance } from './component'
import { EMPTY_OBJ, hasOwn, isArray } from '@vue/shared'
-import { resolveSource } from './componentProps'
+import { type RawProps, resolveSource } from './componentProps'
+import { interopKey } from './vdomInterop'
/**
* The logic from core isn't too reusable so it's better to duplicate here
)
}
-function propGetter(rawProps: Record<string, any>, key: string) {
+function propGetter(rawProps: RawProps, key: string) {
const dynamicSources = rawProps.$
if (dynamicSources) {
let i = dynamicSources.length
while (i--) {
const source = resolveSource(dynamicSources[i])
- if (hasOwn(source, key)) return resolveSource(source[key])
+ if (hasOwn(source, key))
+ // for props passed from VDOM component, no need to resolve
+ return dynamicSources[interopKey]
+ ? source[key]
+ : resolveSource(source[key])
}
}
return rawProps[key] && resolveSource(rawProps[key])
import { ReactiveFlags } from '@vue/reactivity'
import { normalizeEmitsOptions } from './componentEmits'
import { renderEffect } from './renderEffect'
+import type { interopKey } from './vdomInterop'
export type RawProps = Record<string, () => unknown> & {
// generated by compiler for :[key]="x" or v-bind="x"
- $?: DynamicPropsSource[]
+ $?: DynamicPropsSource[] & { [interopKey]?: boolean }
}
export type DynamicPropsSource =
import { createTextNode } from './dom/node'
import { optimizePropertyLookup } from './dom/prop'
+export const interopKey: unique symbol = Symbol(`interop`)
+
// mounting vapor components and slots in vdom
const vaporInteropImpl: Omit<
VaporInteropInterface,
const propsRef = shallowRef(vnode.props)
const slotsRef = shallowRef(vnode.children)
+ const dynamicPropSource: (() => any)[] & { [interopKey]?: boolean } = [
+ () => propsRef.value,
+ ]
+ // mark as interop props
+ dynamicPropSource[interopKey] = true
// @ts-expect-error
const instance = (vnode.component = createComponent(
vnode.type as any as VaporComponent,
{
- $: [() => propsRef.value],
+ $: dynamicPropSource,
} as RawProps,
{
_: slotsRef, // pass the slots ref