From e16e9a7341e7cfb3c443da4e5e5b06e8158712c3 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 11 Oct 2024 09:52:06 +0800 Subject: [PATCH 01/32] fix(custom-element): properly remove hyphenated attribute (#12143) close #12139 --- .../__tests__/customElement.spec.ts | 35 +++++++++++++++++++ packages/runtime-dom/src/modules/props.ts | 3 +- packages/runtime-dom/src/patchProp.ts | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index ef5051f42..6b9f7d139 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -1386,4 +1386,39 @@ describe('defineCustomElement', () => { await nextTick() expect(e.shadowRoot!.innerHTML).toBe(`false,boolean`) }) + + test('hyphenated attr removal', async () => { + const E = defineCustomElement({ + props: { + fooBar: { + type: Boolean, + }, + }, + render() { + return this.fooBar + }, + }) + customElements.define('el-hyphenated-attr-removal', E) + const toggle = ref(true) + const Comp = { + render() { + return h('el-hyphenated-attr-removal', { + 'foo-bar': toggle.value ? '' : null, + }) + }, + } + render(h(Comp), container) + const el = container.children[0] + expect(el.hasAttribute('foo-bar')).toBe(true) + expect((el as any).outerHTML).toBe( + ``, + ) + + toggle.value = false + await nextTick() + expect(el.hasAttribute('foo-bar')).toBe(false) + expect((el as any).outerHTML).toBe( + ``, + ) + }) }) diff --git a/packages/runtime-dom/src/modules/props.ts b/packages/runtime-dom/src/modules/props.ts index 93d45c9e1..98608831a 100644 --- a/packages/runtime-dom/src/modules/props.ts +++ b/packages/runtime-dom/src/modules/props.ts @@ -8,6 +8,7 @@ export function patchDOMProp( key: string, value: any, parentComponent: any, + attrName?: string, ): void { // __UNSAFE__ // Reason: potentially setting innerHTML. @@ -106,5 +107,5 @@ export function patchDOMProp( ) } } - needRemove && el.removeAttribute(key) + needRemove && el.removeAttribute(attrName || key) } diff --git a/packages/runtime-dom/src/patchProp.ts b/packages/runtime-dom/src/patchProp.ts index 5814e77c4..b6af89971 100644 --- a/packages/runtime-dom/src/patchProp.ts +++ b/packages/runtime-dom/src/patchProp.ts @@ -62,7 +62,7 @@ export const patchProp: DOMRendererOptions['patchProp'] = ( (el as VueElement)._isVueCE && (/[A-Z]/.test(key) || !isString(nextValue)) ) { - patchDOMProp(el, camelize(key), nextValue, parentComponent) + patchDOMProp(el, camelize(key), nextValue, parentComponent, key) } else { // special case for with // :true-value & :false-value From d9d4d4e158cd51a9ddda249f29de8467f60b2792 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 11 Oct 2024 10:28:54 +0800 Subject: [PATCH 02/32] fix(runtime-core): allow symbol values for slot prop key (#12069) close #12068 --- .../__tests__/helpers/renderSlot.spec.ts | 6 ++++++ packages/runtime-core/src/helpers/renderSlot.ts | 13 +++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts b/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts index c4ae077ba..bc0678b51 100644 --- a/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts +++ b/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts @@ -32,6 +32,12 @@ describe('renderSlot', () => { expect(vnode.key).toBe('foo') }) + it('should allow symbol values for slot prop key', () => { + const key = Symbol() + const vnode = renderSlot({ default: () => [h('div')] }, 'default', { key }) + expect(vnode.key).toBe('_default') + }) + it('should render slot fallback', () => { const vnode = renderSlot({}, 'default', { key: 'foo' }, () => ['fallback']) expect(vnode.children).toEqual(['fallback']) diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts index 2cb266ef1..92f7dab36 100644 --- a/packages/runtime-core/src/helpers/renderSlot.ts +++ b/packages/runtime-core/src/helpers/renderSlot.ts @@ -14,7 +14,7 @@ import { isVNode, openBlock, } from '../vnode' -import { PatchFlags, SlotFlags } from '@vue/shared' +import { PatchFlags, SlotFlags, isSymbol } from '@vue/shared' import { warn } from '../warning' import { isAsyncWrapper } from '../apiAsyncComponent' @@ -72,15 +72,16 @@ export function renderSlot( } openBlock() const validSlotContent = slot && ensureValidVNode(slot(props)) + const slotKey = + props.key || + // slot content array of a dynamic conditional slot may have a branch + // key attached in the `createSlots` helper, respect that + (validSlotContent && (validSlotContent as any).key) const rendered = createBlock( Fragment, { key: - (props.key || - // slot content array of a dynamic conditional slot may have a branch - // key attached in the `createSlots` helper, respect that - (validSlotContent && (validSlotContent as any).key) || - `_${name}`) + + (slotKey && !isSymbol(slotKey) ? slotKey : `_${name}`) + // #7256 force differentiate fallback content from actual content (!validSlotContent && fallback ? '_fb' : ''), }, From 704173e24276706de672cca6c9507e4dd9651197 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 11 Oct 2024 10:30:09 +0800 Subject: [PATCH 03/32] fix(types): ensure `this.$props` type does not include `string` (#12123) close #12122 --- packages-private/dts-test/defineComponent.test-d.tsx | 10 ++++++++++ packages/runtime-core/src/apiDefineComponent.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 9b4c18471..fda3ca485 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -2068,3 +2068,13 @@ expectString(instance.actionText) // public prop on $props should be optional // @ts-expect-error expectString(instance.$props.actionText) + +// #12122 +defineComponent({ + props: { foo: String }, + render() { + expectType<{ readonly foo?: string }>(this.$props) + // @ts-expect-error + expectType(this.$props) + }, +}) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 9ae6b41a4..2ce870f01 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -265,7 +265,7 @@ export function defineComponent< Mixin, Extends, ResolvedEmits, - RuntimeEmitsKeys, + {}, {}, false, InjectOptions, From e0a591e1cdf842631104f16ce5753e487c08e439 Mon Sep 17 00:00:00 2001 From: w2xi <57785259+w2xi@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:31:01 +0800 Subject: [PATCH 04/32] chore(sfc-playground): adjust the tooltip text for toggling the theme (#12116) * chore(sfc-playground): adjust the tooltip text for toggling the theme * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- packages-private/sfc-playground/src/App.vue | 1 + packages-private/sfc-playground/src/Header.vue | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages-private/sfc-playground/src/App.vue b/packages-private/sfc-playground/src/App.vue index c9295d41b..d163b1a3e 100644 --- a/packages-private/sfc-playground/src/App.vue +++ b/packages-private/sfc-playground/src/App.vue @@ -123,6 +123,7 @@ onMounted(() => { :prod="productionMode" :ssr="useSSRMode" :autoSave="autoSave" + :theme="theme" @toggle-theme="toggleTheme" @toggle-prod="toggleProdMode" @toggle-ssr="toggleSSR" diff --git a/packages-private/sfc-playground/src/Header.vue b/packages-private/sfc-playground/src/Header.vue index 2778724b0..aea6c022a 100644 --- a/packages-private/sfc-playground/src/Header.vue +++ b/packages-private/sfc-playground/src/Header.vue @@ -15,6 +15,7 @@ const props = defineProps<{ prod: boolean ssr: boolean autoSave: boolean + theme: 'dark' | 'light' }>() const emit = defineEmits([ 'toggle-theme', @@ -117,7 +118,11 @@ function toggleDark() { > {{ autoSave ? 'AutoSave ON' : 'AutoSave OFF' }} - From f1a4f67aedfe83e440c54222213f070774faa421 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 11 Oct 2024 10:34:28 +0800 Subject: [PATCH 05/32] fix(transition/ssr): make transition appear work with Suspense in SSR (#12047) close #12046 --- .../runtime-core/__tests__/hydration.spec.ts | 30 +++++++++++++++++++ packages/runtime-core/src/hydration.ts | 5 +++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index c98f1f473..a1fb8cde3 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -1613,6 +1613,36 @@ describe('SSR hydration', () => { `) }) + test('Suspense + transition appear', async () => { + const { vnode, container } = mountWithHydration( + ``, + () => + h(Suspense, {}, () => + h( + Transition, + { appear: true }, + { + default: () => h('div', 'foo'), + }, + ), + ), + ) + + expect(vnode.el).toBe(container.firstChild) + // wait for hydration to finish + await new Promise(r => setTimeout(r)) + + expect(container.firstChild).toMatchInlineSnapshot(` +
+ foo +
+ `) + await nextTick() + expect(vnode.el).toBe(container.firstChild) + }) + // #10607 test('update component stable slot (prod + optimized mode)', async () => { __DEV__ = false diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 82972e171..c49db529c 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -385,7 +385,10 @@ export function createHydrationFunctions( let needCallTransitionHooks = false if (isTemplateNode(el)) { needCallTransitionHooks = - needTransition(parentSuspense, transition) && + needTransition( + null, // no need check parentSuspense in hydration + transition, + ) && parentComponent && parentComponent.vnode.props && parentComponent.vnode.props.appear From c0418a3b8fa96a0b108ab71b7aab5d3388f90557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:35:57 +0800 Subject: [PATCH 06/32] fix(defineModel): handle kebab-case model correctly (#12063) close #12060 --- .../runtime-core/__tests__/helpers/useModel.spec.ts | 12 ++++++------ packages/runtime-core/src/helpers/useModel.ts | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/runtime-core/__tests__/helpers/useModel.spec.ts b/packages/runtime-core/__tests__/helpers/useModel.spec.ts index 3c724b0ba..8d31848e1 100644 --- a/packages/runtime-core/__tests__/helpers/useModel.spec.ts +++ b/packages/runtime-core/__tests__/helpers/useModel.spec.ts @@ -153,10 +153,10 @@ describe('useModel', () => { const compRender = vi.fn() const Comp = defineComponent({ - props: ['fooBar'], - emits: ['update:fooBar'], + props: ['foo-bar'], + emits: ['update:foo-bar'], setup(props) { - foo = useModel(props, 'fooBar') + foo = useModel(props, 'foo-bar') return () => { compRender() return foo.value @@ -192,10 +192,10 @@ describe('useModel', () => { const compRender = vi.fn() const Comp = defineComponent({ - props: ['fooBar'], - emits: ['update:fooBar'], + props: ['foo-bar'], + emits: ['update:foo-bar'], setup(props) { - foo = useModel(props, 'fooBar') + foo = useModel(props, 'foo-bar') return () => { compRender() return foo.value diff --git a/packages/runtime-core/src/helpers/useModel.ts b/packages/runtime-core/src/helpers/useModel.ts index 37fd1d719..c40938ead 100644 --- a/packages/runtime-core/src/helpers/useModel.ts +++ b/packages/runtime-core/src/helpers/useModel.ts @@ -28,14 +28,14 @@ export function useModel( return ref() as any } - if (__DEV__ && !(i.propsOptions[0] as NormalizedProps)[name]) { + const camelizedName = camelize(name) + if (__DEV__ && !(i.propsOptions[0] as NormalizedProps)[camelizedName]) { warn(`useModel() called with prop "${name}" which is not declared.`) return ref() as any } - const camelizedName = camelize(name) const hyphenatedName = hyphenate(name) - const modifiers = getModelModifiers(props, name) + const modifiers = getModelModifiers(props, camelizedName) const res = customRef((track, trigger) => { let localValue: any @@ -43,7 +43,7 @@ export function useModel( let prevEmittedValue: any watchSyncEffect(() => { - const propValue = props[name] + const propValue = props[camelizedName] if (hasChanged(localValue, propValue)) { localValue = propValue trigger() From 7ad289e1e7fea654524008ff91e43a8b8a55ef22 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 11 Oct 2024 10:39:08 +0800 Subject: [PATCH 07/32] fix(reactivity): trigger reactivity for Map key `undefined` (#12055) close #12054 --- packages/reactivity/__tests__/reactive.spec.ts | 10 ++++++++++ packages/reactivity/src/dep.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/__tests__/reactive.spec.ts b/packages/reactivity/__tests__/reactive.spec.ts index 47f6aa297..aabd95456 100644 --- a/packages/reactivity/__tests__/reactive.spec.ts +++ b/packages/reactivity/__tests__/reactive.spec.ts @@ -409,4 +409,14 @@ describe('reactivity/reactive', () => { e.effect.stop() expect(targetMap.get(obj)?.get('x')).toBeFalsy() }) + + test('should trigger reactivity when Map key is undefined', () => { + const map = reactive(new Map()) + const c = computed(() => map.get(void 0)) + + expect(c.value).toBe(void 0) + + map.set(void 0, 1) + expect(c.value).toBe(1) + }) }) diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 7e4048743..196c2aaf9 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -340,7 +340,7 @@ export function trigger( }) } else { // schedule runs for SET | ADD | DELETE - if (key !== void 0) { + if (key !== void 0 || depsMap.has(void 0)) { run(depsMap.get(key)) } From f6d99262364b7444ebab8742158599e8cdd79eaa Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 11 Oct 2024 10:41:55 +0800 Subject: [PATCH 08/32] fix(compiler-dom): avoid stringify option with null value (#12096) close #12093 --- .../__snapshots__/stringifyStatic.spec.ts.snap | 17 +++++++++++++++++ .../transforms/stringifyStatic.spec.ts | 11 +++++++++++ .../src/transforms/stringifyStatic.ts | 3 +-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap index 78b576af5..a863eb32e 100644 --- a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap +++ b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap @@ -32,6 +32,23 @@ return function render(_ctx, _cache) { }" `; +exports[`stringify static html > should bail for