diff --git a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts index 3493ec117f3..7ef9b62a36d 100644 --- a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts +++ b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts @@ -640,6 +640,74 @@ describe('vdomInterop', () => { '
boolean:false:false
boolean:false:false
', ) }) + + test('vnode props of a vdom component are a plain object', async () => { + // e.g. Vuetify's useProxiedModel checks `vm.vnode.props?.hasOwnProperty()` + const Field = defineComponent({ + props: ['modelValue'], + setup(props) { + const vm = getCurrentInstance()! + expect(vm.vnode.props!.hasOwnProperty('modelValue')).toBe(true) + expect(vm.vnode.props!.hasOwnProperty('onUpdate:modelValue')).toBe( + true, + ) + return () => + h( + 'i', + `${props.modelValue}:${vm.vnode.props!.hasOwnProperty('modelValue')}:${vm.vnode.props!.hasOwnProperty('onUpdate:modelValue')}`, + ) + }, + }) + const { vdom, vapor } = await renderParity( + { + App: ``, + }, + () => ref({ n: 1 }), + async data => { + data.value.n++ + }, + { Field }, + ) + expect(vdom.after).toBe('2:true:true') + expect(vapor.after).toBe(vdom.after) + }) + + test.each([null, { inherited: true }, 'value'])( + 'preserves an own __proto__ vnode prop with value %j', + async value => { + let vm: ReturnType + const Child = defineComponent({ + inheritAttrs: false, + setup() { + vm = getCurrentInstance()! + return () => null + }, + }) + await renderParity( + { + App: ``, + }, + () => shallowRef({ key: '__proto__', value }), + async data => { + expect(Object.getPrototypeOf(vm!.vnode.props)).toBe( + Object.prototype, + ) + expect(vm!.vnode.props!.hasOwnProperty('__proto__')).toBe(true) + expect(vm!.vnode.props!.__proto__).toBe(value) + + data.value = { key: '__proto__', value: { updated: true } } + await nextTick() + + expect(Object.getPrototypeOf(vm!.vnode.props)).toBe( + Object.prototype, + ) + expect(vm!.vnode.props!.hasOwnProperty('__proto__')).toBe(true) + expect(vm!.vnode.props!.__proto__).toBe(data.value.value) + }, + { Child }, + ) + }, + ) }) describe('v-model', () => { diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index c4ceada475d..d8138aba944 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -811,7 +811,8 @@ function updateProps( triggerPropsValue(instance, RAW_VALUES_KEY) } const vnode = isInteropEnabled && instance.interopVNode - if (vnode && vnode.vi) vnode.props = rawValues + // vdom code reads vnode props as a plain object (e.g. `props.hasOwnProperty`) + if (vnode && vnode.vi) vnode.props = { ...rawValues } const present: Record | undefined = propsToUpdate || isInitial ? undefined : Object.create(null)