From bc167b5c6c7c5756f3b7720a7d3ddcdb2c7f717f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Thu, 20 Oct 2022 21:45:05 +0200 Subject: [PATCH 01/97] fix(runtime-core): watching multiple values - handle `undefined` as initial values (fix: #5032) (#5033) --- .../runtime-core/__tests__/apiWatch.spec.ts | 17 +++++++++++++++++ packages/runtime-core/src/apiWatch.ts | 9 +++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 86ad948adf6..c9db3657367 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -177,6 +177,23 @@ describe('api: watch', () => { ]) }) + it('watching multiple sources: undefined initial values and immediate: true', async () => { + const a = ref() + const b = ref() + let called = false + watch( + [a, b], + (newVal, oldVal) => { + called = true + expect(newVal).toMatchObject([undefined, undefined]) + expect(oldVal).toBeUndefined() + }, + { immediate: true } + ) + await nextTick() + expect(called).toBe(true) + }) + it('watching multiple sources: readonly array', async () => { const state = reactive({ count: 1 }) const status = ref(false) diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 27215f342b3..19026cf645d 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -296,7 +296,9 @@ function doWatch( return NOOP } - let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE + let oldValue: any = isMultiSource + ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE) + : INITIAL_WATCHER_VALUE const job: SchedulerJob = () => { if (!effect.active) { return @@ -323,7 +325,10 @@ function doWatch( callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [ newValue, // pass undefined as the old value when it's changed for the first time - oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue, + oldValue === INITIAL_WATCHER_VALUE || + (isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE) + ? undefined + : oldValue, onCleanup ]) oldValue = newValue From e5fc7dcc02f2dd3fa8172958259049031626375f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Thu, 20 Oct 2022 21:45:51 +0200 Subject: [PATCH 02/97] fix(types): ensure createBlock() helper accepts Teleport and Supsense types (fix: #2855) (#5458) Co-authored-by: Carlos Rodrigues --- packages/runtime-core/src/vnode.ts | 5 ++++- test-dts/compiler.test-d.ts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test-dts/compiler.test-d.ts diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index b731dc0b51f..2122f9f855a 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -24,6 +24,7 @@ import { RawSlots } from './componentSlots' import { isProxy, Ref, toRaw, ReactiveFlags, isRef } from '@vue/reactivity' import { AppContext } from './apiCreateApp' import { + Suspense, SuspenseImpl, isSuspense, SuspenseBoundary @@ -31,7 +32,7 @@ import { import { DirectiveBinding } from './directives' import { TransitionHooks } from './components/BaseTransition' import { warn } from './warning' -import { TeleportImpl, isTeleport } from './components/Teleport' +import { Teleport, TeleportImpl, isTeleport } from './components/Teleport' import { currentRenderingInstance, currentScopeId @@ -63,7 +64,9 @@ export type VNodeTypes = | typeof Static | typeof Comment | typeof Fragment + | typeof Teleport | typeof TeleportImpl + | typeof Suspense | typeof SuspenseImpl export type VNodeRef = diff --git a/test-dts/compiler.test-d.ts b/test-dts/compiler.test-d.ts new file mode 100644 index 00000000000..974b49492a4 --- /dev/null +++ b/test-dts/compiler.test-d.ts @@ -0,0 +1,20 @@ +import { + expectType, + createBlock, + VNode, + Teleport, + Text, + Static, + Comment, + Fragment, + Suspense, + defineComponent +} from './index' + +expectType(createBlock(Teleport)) +expectType(createBlock(Text)) +expectType(createBlock(Static)) +expectType(createBlock(Comment)) +expectType(createBlock(Fragment)) +expectType(createBlock(Suspense)) +expectType(createBlock(defineComponent({}))) From e6224f4256be2fdac3651ade87f9f91ccf6def71 Mon Sep 17 00:00:00 2001 From: bcq028 <108849949+bcq028@users.noreply.github.com> Date: Fri, 21 Oct 2022 15:25:06 +0800 Subject: [PATCH 03/97] fix(reactivity): enable trigger when use str to set length of arr (#6810) Co-authored-by: Anthony Fu --- packages/reactivity/__tests__/effect.spec.ts | 16 ++++++++++++++++ packages/reactivity/src/effect.ts | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index a66aff278a4..a322c7209f4 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -922,6 +922,22 @@ describe('reactivity/effect', () => { expect(fnSpy2).toHaveBeenCalledTimes(1) }) + it('should be triggered when set length with string', () => { + let ret1 = 'idle' + let ret2 = 'idle' + const arr1 = reactive(new Array(11).fill(0)) + const arr2 = reactive(new Array(11).fill(0)) + effect(() => { + ret1 = arr1[10] === undefined ? 'arr[10] is set to empty' : 'idle' + }) + effect(() => { + ret2 = arr2[10] === undefined ? 'arr[10] is set to empty' : 'idle' + }) + arr1.length = 2 + arr2.length = '2' as any + expect(ret1).toBe(ret2) + }) + describe('readonly + reactive for Map', () => { test('should work with readonly(reactive(Map))', () => { const m = reactive(new Map()) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 34b53eb8fef..8a54372cd5b 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,5 +1,5 @@ import { TrackOpTypes, TriggerOpTypes } from './operations' -import { extend, isArray, isIntegerKey, isMap } from '@vue/shared' +import { extend, isArray, isIntegerKey, isMap, toNumber } from '@vue/shared' import { EffectScope, recordEffectScope } from './effectScope' import { createDep, @@ -277,7 +277,7 @@ export function trigger( deps = [...depsMap.values()] } else if (key === 'length' && isArray(target)) { depsMap.forEach((dep, key) => { - if (key === 'length' || key >= (newValue as number)) { + if (key === 'length' || key >= toNumber(newValue)) { deps.push(dep) } }) From 54b6ba32cafcc41fa9b7b85f1f1a306923204177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Sat, 22 Oct 2022 11:20:02 +0200 Subject: [PATCH 04/97] fix(runtime-core): ensure props definition objects are not mutated during props normalization (close: #6915) (#6916) --- .../__tests__/componentProps.spec.ts | 19 +++++++++++++++++++ packages/runtime-core/src/componentProps.ts | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index 40725dfcd13..cdb77838e31 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -595,4 +595,23 @@ describe('component props', () => { JSON.stringify(attrs) + Object.keys(attrs) ) }) + + // #691ef + test('should not mutate original props long-form definition object', () => { + const props = { + msg: { + type: String + } + } + const Comp = defineComponent({ + props, + render() {} + }) + + const root = nodeOps.createElement('div') + + render(h(Comp, { msg: 'test' }), root) + + expect(Object.keys(props.msg).length).toBe(1) + }) }) diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 09b487811b5..d59a4e94699 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -522,7 +522,7 @@ export function normalizePropsOptions( if (validatePropName(normalizedKey)) { const opt = raw[key] const prop: NormalizedProp = (normalized[normalizedKey] = - isArray(opt) || isFunction(opt) ? { type: opt } : opt) + isArray(opt) || isFunction(opt) ? { type: opt } : { ...opt }) if (prop) { const booleanIndex = getTypeIndex(Boolean, prop.type) const stringIndex = getTypeIndex(String, prop.type) From b0b74a160c08941fa9a7a5460f36a1f2fccbf423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20L=C3=BCnborg?= Date: Sat, 22 Oct 2022 11:20:46 +0200 Subject: [PATCH 05/97] fix(runtime-core): custom-element: ensure number casting of camelCase props. (fix: #5374) (#5377) --- packages/runtime-dom/__tests__/customElement.spec.ts | 10 +++++----- packages/runtime-dom/src/apiCustomElement.ts | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index e29c36123f3..300cc2322ce 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -135,14 +135,14 @@ describe('defineCustomElement', () => { test('attribute -> prop type casting', async () => { const E = defineCustomElement({ props: { - foo: Number, + fooBar: Number, // test casting of camelCase prop names bar: Boolean, baz: String }, render() { return [ - this.foo, - typeof this.foo, + this.fooBar, + typeof this.fooBar, this.bar, typeof this.bar, this.baz, @@ -151,7 +151,7 @@ describe('defineCustomElement', () => { } }) customElements.define('my-el-props-cast', E) - container.innerHTML = `` + container.innerHTML = `` const e = container.childNodes[0] as VueElement expect(e.shadowRoot!.innerHTML).toBe( `1 number false boolean 12345 string` @@ -161,7 +161,7 @@ describe('defineCustomElement', () => { await nextTick() expect(e.shadowRoot!.innerHTML).toBe(`1 number true boolean 12345 string`) - e.setAttribute('foo', '2e1') + e.setAttribute('foo-bar', '2e1') await nextTick() expect(e.shadowRoot!.innerHTML).toBe( `20 number true boolean 12345 string` diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index eabe83b6b9f..5ff45d652f2 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -268,10 +268,11 @@ export class VueElement extends BaseClass { protected _setAttr(key: string) { let value = this.getAttribute(key) - if (this._numberProps && this._numberProps[key]) { + const camelKey = camelize(key) + if (this._numberProps && this._numberProps[camelKey]) { value = toNumber(value) } - this._setProp(camelize(key), value, false) + this._setProp(camelKey, value, false) } /** From cbc3e67c375ed75fb4ef605c6d5a823890a29712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=95=E8=AF=81?= Date: Wed, 26 Oct 2022 15:01:37 +0800 Subject: [PATCH 06/97] types(shared): Improve LooseRequired (#6244) --- packages/shared/src/typeUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/typeUtils.ts b/packages/shared/src/typeUtils.ts index 8730d7f38bf..3f3620c6672 100644 --- a/packages/shared/src/typeUtils.ts +++ b/packages/shared/src/typeUtils.ts @@ -5,7 +5,7 @@ export type UnionToIntersection = ( : never // make keys required but keep undefined values -export type LooseRequired = { [P in string & keyof T]: T[P] } +export type LooseRequired = { [P in keyof (T & Required)]: T[P] } // If the the type T accepts type "any", output type Y, otherwise output type N. // https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360 From 8e792d93a8062e29d9da279eee2202f38897f42f Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Wed, 26 Oct 2022 08:07:44 +0100 Subject: [PATCH 07/97] types(h): Support passing `props` to `Component` when using `h` (#3219) --- packages/runtime-core/src/h.ts | 6 +++--- test-dts/h.test-d.ts | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index 44f91340827..520f568e7a8 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -141,9 +141,9 @@ export function h

( ): VNode // component without props -export function h( - type: Component, - props: null, +export function h

( + type: Component

, + props?: (RawProps & P) | null, children?: RawChildren | RawSlots ): VNode diff --git a/test-dts/h.test-d.ts b/test-dts/h.test-d.ts index 5318f71fe94..6116fff74a9 100644 --- a/test-dts/h.test-d.ts +++ b/test-dts/h.test-d.ts @@ -145,12 +145,11 @@ describe('h inference w/ defineComponent', () => { // expectError(h(Foo, { bar: 1, foo: 1 })) // }) -// #922 +// #922 and #3218 describe('h support for generic component type', () => { function foo(bar: Component) { h(bar) h(bar, 'hello') - // @ts-expect-error h(bar, { id: 'ok' }, 'hello') } foo({}) From d9de6caecdada62b29ad6836c0580b119f47523c Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 26 Oct 2022 15:17:25 +0800 Subject: [PATCH 08/97] refactor: move toNumber call out of loop --- packages/reactivity/src/effect.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 8a54372cd5b..f1799a62d3a 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -276,8 +276,9 @@ export function trigger( // trigger all effects for target deps = [...depsMap.values()] } else if (key === 'length' && isArray(target)) { + const newLength = toNumber(newValue) depsMap.forEach((dep, key) => { - if (key === 'length' || key >= toNumber(newValue)) { + if (key === 'length' || key >= newLength) { deps.push(dep) } }) From 09bb3e996ef17967022243a519d7dcc2921dd049 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 26 Oct 2022 16:18:19 +0800 Subject: [PATCH 09/97] fix(compiler-ssr): fix invalid codegen when v-slot name is explicit empty attr (#3326) squashed from fix by @tjk --- packages/compiler-core/src/index.ts | 3 ++- .../compiler-core/src/transforms/transformExpression.ts | 2 +- packages/compiler-ssr/__tests__/ssrComponent.spec.ts | 5 +++++ .../compiler-ssr/src/transforms/ssrTransformComponent.ts | 6 ++++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/compiler-core/src/index.ts b/packages/compiler-core/src/index.ts index 6ed7aa5b897..6a1f8b63b57 100644 --- a/packages/compiler-core/src/index.ts +++ b/packages/compiler-core/src/index.ts @@ -43,7 +43,8 @@ export { processIf } from './transforms/vIf' export { processFor, createForLoopParams } from './transforms/vFor' export { transformExpression, - processExpression + processExpression, + stringifyExpression } from './transforms/transformExpression' export { buildSlots, diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts index e4311ad4f88..43c69559688 100644 --- a/packages/compiler-core/src/transforms/transformExpression.ts +++ b/packages/compiler-core/src/transforms/transformExpression.ts @@ -361,7 +361,7 @@ function canPrefix(id: Identifier) { return true } -function stringifyExpression(exp: ExpressionNode | string): string { +export function stringifyExpression(exp: ExpressionNode | string): string { if (isString(exp)) { return exp } else if (exp.type === NodeTypes.SIMPLE_EXPRESSION) { diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts index 5d5191ffb44..cd21e48cb9a 100644 --- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts @@ -104,6 +104,11 @@ describe('ssr: components', () => { `) }) + test('empty attribute should not produce syntax error', () => { + // previously this would produce syntax error `default: _withCtx((, _push, ...)` + expect(compile(`foo`).code).not.toMatch(`(,`) + }) + test('named slots', () => { expect( compile(` diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index df190c768a8..dc8c6a4ae4f 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -36,7 +36,8 @@ import { CallExpression, JSChildNode, RESOLVE_DYNAMIC_COMPONENT, - TRANSITION + TRANSITION, + stringifyExpression } from '@vue/compiler-dom' import { SSR_RENDER_COMPONENT, SSR_RENDER_VNODE } from '../runtimeHelpers' import { @@ -145,8 +146,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => { wipMap.set(node, wipEntries) const buildSSRSlotFn: SlotFnBuilder = (props, children, loc) => { + const param0 = (props && stringifyExpression(props)) || `_` const fn = createFunctionExpression( - [props || `_`, `_push`, `_parent`, `_scopeId`], + [param0, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later true, // newline true, // isSlot From e816812f10b9e3a375eef8dffd617d7f08b23c00 Mon Sep 17 00:00:00 2001 From: HeYunfei Date: Wed, 26 Oct 2022 16:27:42 +0800 Subject: [PATCH 10/97] fix(types): should unwrap tuple correctly (#3820) fix #3819 --- packages/reactivity/src/ref.ts | 13 ++-- test-dts/reactivity.test-d.ts | 135 ++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 70 deletions(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 5843b3f63ae..2632160b318 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -280,13 +280,10 @@ export interface RefUnwrapBailTypes {} export type ShallowUnwrapRef = { [K in keyof T]: T[K] extends Ref - ? V - : // if `V` is `unknown` that means it does not extend `Ref` and is undefined - T[K] extends Ref | undefined - ? unknown extends V - ? undefined - : V | undefined - : T[K] + ? V // if `V` is `unknown` that means it does not extend `Ref` and is undefined + : T[K] extends Ref | undefined + ? unknown extends V ? undefined : V | undefined + : T[K] } export type UnwrapRef = T extends ShallowRef @@ -303,7 +300,7 @@ export type UnwrapRefSimple = T extends | RefUnwrapBailTypes[keyof RefUnwrapBailTypes] | { [RawSymbol]?: true } ? T - : T extends Array + : T extends ReadonlyArray ? { [K in keyof T]: UnwrapRefSimple } : T extends object & { [ShallowReactiveMarker]?: never } ? { diff --git a/test-dts/reactivity.test-d.ts b/test-dts/reactivity.test-d.ts index 0499c6da287..8722441ec14 100644 --- a/test-dts/reactivity.test-d.ts +++ b/test-dts/reactivity.test-d.ts @@ -1,62 +1,73 @@ -import { - ref, - readonly, - shallowReadonly, - describe, - expectError, - expectType, - Ref, - reactive, - markRaw -} from './index' - -describe('should support DeepReadonly', () => { - const r = readonly({ obj: { k: 'v' } }) - // @ts-expect-error - expectError((r.obj = {})) - // @ts-expect-error - expectError((r.obj.k = 'x')) -}) - -// #4180 -describe('readonly ref', () => { - const r = readonly(ref({ count: 1 })) - expectType(r) -}) - -describe('should support markRaw', () => { - class Test { - item = {} as Ref - } - const test = new Test() - const plain = { - ref: ref(1) - } - - const r = reactive({ - class: { - raw: markRaw(test), - reactive: test - }, - plain: { - raw: markRaw(plain), - reactive: plain - } - }) - - expectType>(r.class.raw) - // @ts-expect-error it should unwrap - expectType>(r.class.reactive) - - expectType>(r.plain.raw.ref) - // @ts-expect-error it should unwrap - expectType>(r.plain.reactive.ref) -}) - -describe('shallowReadonly ref unwrap', () => { - const r = shallowReadonly({ count: { n: ref(1) } }) - // @ts-expect-error - r.count = 2 - expectType(r.count.n) - r.count.n.value = 123 -}) +import { + ref, + readonly, + shallowReadonly, + describe, + expectError, + expectType, + Ref, + reactive, + markRaw +} from './index' + +describe('should support DeepReadonly', () => { + const r = readonly({ obj: { k: 'v' } }) + // @ts-expect-error + expectError((r.obj = {})) + // @ts-expect-error + expectError((r.obj.k = 'x')) +}) + +// #4180 +describe('readonly ref', () => { + const r = readonly(ref({ count: 1 })) + expectType(r) +}) + +describe('should support markRaw', () => { + class Test { + item = {} as Ref + } + const test = new Test() + const plain = { + ref: ref(1) + } + + const r = reactive({ + class: { + raw: markRaw(test), + reactive: test + }, + plain: { + raw: markRaw(plain), + reactive: plain + } + }) + + expectType>(r.class.raw) + // @ts-expect-error it should unwrap + expectType>(r.class.reactive) + + expectType>(r.plain.raw.ref) + // @ts-expect-error it should unwrap + expectType>(r.plain.reactive.ref) +}) + +describe('shallowReadonly ref unwrap', () => { + const r = shallowReadonly({ count: { n: ref(1) } }) + // @ts-expect-error + r.count = 2 + expectType(r.count.n) + r.count.n.value = 123 +}) + +// #3819 +describe('should unwrap tuple correctly', () => { + const readonlyTuple = [ref(0)] as const + const reactiveReadonlyTuple = reactive(readonlyTuple) + expectType>(reactiveReadonlyTuple[0]) + + const tuple: [Ref] = [ref(0)] + const reactiveTuple = reactive(tuple) + expectType>(reactiveTuple[0]) +}) From 183e4e61523b35edc3064a033734825c87a400d7 Mon Sep 17 00:00:00 2001 From: webfansplz <308241863@qq.com> Date: Wed, 26 Oct 2022 16:31:09 +0800 Subject: [PATCH 11/97] refactor(types): use template literal types insteads of any (#4166) --- .../runtime-dom/src/components/Transition.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts index 0c1a30f6b95..2c483c76e72 100644 --- a/packages/runtime-dom/src/components/Transition.ts +++ b/packages/runtime-dom/src/components/Transition.ts @@ -12,9 +12,11 @@ import { isObject, toNumber, extend, isArray } from '@vue/shared' const TRANSITION = 'transition' const ANIMATION = 'animation' +type AnimationTypes = typeof TRANSITION | typeof ANIMATION + export interface TransitionProps extends BaseTransitionProps { name?: string - type?: typeof TRANSITION | typeof ANIMATION + type?: AnimationTypes css?: boolean duration?: number | { enter: number; leave: number } // custom transition classes @@ -368,24 +370,33 @@ function whenTransitionEnds( } interface CSSTransitionInfo { - type: typeof TRANSITION | typeof ANIMATION | null + type: AnimationTypes | null propCount: number timeout: number hasTransform: boolean } +type AnimationProperties = 'Delay' | 'Duration' +type StylePropertiesKey = + | `${AnimationTypes}${AnimationProperties}` + | `${typeof TRANSITION}Property` + export function getTransitionInfo( el: Element, expectedType?: TransitionProps['type'] ): CSSTransitionInfo { - const styles: any = window.getComputedStyle(el) + const styles = window.getComputedStyle(el) as Pick< + CSSStyleDeclaration, + StylePropertiesKey + > // JSDOM may return undefined for transition properties - const getStyleProperties = (key: string) => (styles[key] || '').split(', ') - const transitionDelays = getStyleProperties(TRANSITION + 'Delay') - const transitionDurations = getStyleProperties(TRANSITION + 'Duration') + const getStyleProperties = (key: StylePropertiesKey) => + (styles[key] || '').split(', ') + const transitionDelays = getStyleProperties(`${TRANSITION}Delay`) + const transitionDurations = getStyleProperties(`${TRANSITION}Duration`) const transitionTimeout = getTimeout(transitionDelays, transitionDurations) - const animationDelays = getStyleProperties(ANIMATION + 'Delay') - const animationDurations = getStyleProperties(ANIMATION + 'Duration') + const animationDelays = getStyleProperties(`${ANIMATION}Delay`) + const animationDurations = getStyleProperties(`${ANIMATION}Duration`) const animationTimeout = getTimeout(animationDelays, animationDurations) let type: CSSTransitionInfo['type'] = null @@ -420,7 +431,9 @@ export function getTransitionInfo( } const hasTransform = type === TRANSITION && - /\b(transform|all)(,|$)/.test(styles[TRANSITION + 'Property']) + /\b(transform|all)(,|$)/.test( + getStyleProperties(`${TRANSITION}Property`).toString() + ) return { type, timeout, From 83f7e6f8a688e823274379fe79f58b90ea58892d Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 26 Oct 2022 16:44:30 +0800 Subject: [PATCH 12/97] fix(compiler-sfc): support using extends interface with defineProps() (#4512) fix #4498 --- .../__snapshots__/compileScript.spec.ts.snap | 27 +++++ .../__tests__/compileScript.spec.ts | 25 ++++ packages/compiler-sfc/src/compileScript.ts | 109 ++++++++++++++---- 3 files changed, 141 insertions(+), 20 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index b7fc9304b26..aa5e2d04926 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -1467,6 +1467,33 @@ export default /*#__PURE__*/_defineComponent({ +return { } +} + +})" +`; + +exports[`SFC compile + + `) + assertCode(content) + expect(content).toMatch(`z: { type: Number, required: true }`) + expect(content).toMatch(`y: { type: String, required: true }`) + expect(content).toMatch(`x: { type: Number, required: false }`) + expect(bindings).toStrictEqual({ + x: BindingTypes.PROPS, + y: BindingTypes.PROPS, + z: BindingTypes.PROPS + }) + }) + test('defineProps w/ exported interface', () => { const { content, bindings } = compile(` + `) + assertCode(content) + expect(content).toMatch(`setup(__props, { expose, emit }) {`) + expect(content).toMatch(`emits: ['foo']`) + }) + test('runtime Enum', () => { const { content, bindings } = compile( ` + `) + assertCode(content) + expect(content).toMatch(`const a = 1;`) // test correct removal + expect(content).toMatch(`props: ['item'],`) + expect(content).toMatch(`emits: ['a'],`) + }) + test('defineProps/defineEmits in multi-variable declaration (full removal)', () => { const { content } = compile(` + `) + expect(bindings).toStrictEqual({ + ref: BindingTypes.SETUP_MAYBE_REF, + reactive: BindingTypes.SETUP_MAYBE_REF, + foo: BindingTypes.SETUP_MAYBE_REF, + bar: BindingTypes.SETUP_MAYBE_REF + }) + }) + + test('import w/ alias', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + _reactive: BindingTypes.SETUP_MAYBE_REF, + _ref: BindingTypes.SETUP_MAYBE_REF, + foo: BindingTypes.SETUP_REF, + bar: BindingTypes.SETUP_REACTIVE_CONST + }) + }) + }) }) // in dev mode, declared bindings are returned as an object from setup() diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 09222396fc8..adc22ab6820 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -129,6 +129,7 @@ export interface SFCScriptCompileOptions { export interface ImportBinding { isType: boolean imported: string + local: string source: string isFromSetup: boolean isUsedInTemplate: boolean @@ -272,7 +273,6 @@ export function compileScript( const bindingMetadata: BindingMetadata = {} const helperImports: Set = new Set() const userImports: Record = Object.create(null) - const userImportAlias: Record = Object.create(null) const scriptBindings: Record = Object.create(null) const setupBindings: Record = Object.create(null) @@ -362,10 +362,6 @@ export function compileScript( isFromSetup: boolean, needTemplateUsageCheck: boolean ) { - if (source === 'vue' && imported) { - userImportAlias[imported] = local - } - // template usage check is only needed in non-inline mode, so we can skip // the work if inlineTemplate is true. let isUsedInTemplate = needTemplateUsageCheck @@ -382,6 +378,7 @@ export function compileScript( userImports[local] = { isType, imported: imported || 'default', + local, source, isFromSetup, isUsedInTemplate @@ -990,7 +987,7 @@ export function compileScript( } } if (node.declaration) { - walkDeclaration(node.declaration, scriptBindings, userImportAlias) + walkDeclaration(node.declaration, scriptBindings, userImports) } } else if ( (node.type === 'VariableDeclaration' || @@ -999,7 +996,7 @@ export function compileScript( node.type === 'TSEnumDeclaration') && !node.declare ) { - walkDeclaration(node, scriptBindings, userImportAlias) + walkDeclaration(node, scriptBindings, userImports) } } @@ -1199,7 +1196,7 @@ export function compileScript( node.type === 'ClassDeclaration') && !node.declare ) { - walkDeclaration(node, setupBindings, userImportAlias) + walkDeclaration(node, setupBindings, userImports) } // walk statements & named exports / variable declarations for top level @@ -1654,8 +1651,17 @@ function registerBinding( function walkDeclaration( node: Declaration, bindings: Record, - userImportAlias: Record + userImports: Record ) { + function getUserBinding(name: string) { + const binding = Object.values(userImports).find( + binding => binding.source === 'vue' && binding.imported === name + ) + if (binding) return binding.local + else if (!userImports[name]) return name + return undefined + } + if (node.type === 'VariableDeclaration') { const isConst = node.kind === 'const' // export const foo = ... @@ -1669,7 +1675,7 @@ function walkDeclaration( ) if (id.type === 'Identifier') { let bindingType - const userReactiveBinding = userImportAlias['reactive'] || 'reactive' + const userReactiveBinding = getUserBinding('reactive') if (isCallOf(init, userReactiveBinding)) { // treat reactive() calls as let since it's meant to be mutable bindingType = isConst @@ -1685,7 +1691,7 @@ function walkDeclaration( ? BindingTypes.SETUP_REACTIVE_CONST : BindingTypes.SETUP_CONST } else if (isConst) { - if (isCallOf(init, userImportAlias['ref'] || 'ref')) { + if (isCallOf(init, getUserBinding('ref'))) { bindingType = BindingTypes.SETUP_REF } else { bindingType = BindingTypes.SETUP_MAYBE_REF @@ -1982,10 +1988,11 @@ function genRuntimeEmits(emits: Set) { function isCallOf( node: Node | null | undefined, - test: string | ((id: string) => boolean) + test: string | ((id: string) => boolean) | null | undefined ): node is CallExpression { return !!( node && + test && node.type === 'CallExpression' && node.callee.type === 'Identifier' && (typeof test === 'string' @@ -1994,7 +2001,7 @@ function isCallOf( ) } -function canNeverBeRef(node: Node, userReactiveImport: string): boolean { +function canNeverBeRef(node: Node, userReactiveImport?: string): boolean { if (isCallOf(node, userReactiveImport)) { return true } From 8d1f526174db277ae5aa9297a43f20a43e991294 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 8 Nov 2022 17:19:45 +0800 Subject: [PATCH 49/97] fix(compiler-sfc): fix binding analysis for aliased late import --- .../__tests__/compileScript.spec.ts | 13 +++ packages/compiler-sfc/src/compileScript.ts | 80 +++++++++++-------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index 5c03ac17fdb..96160e272d5 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -391,6 +391,19 @@ defineExpose({ foo: 123 }) bar: BindingTypes.SETUP_REACTIVE_CONST }) }) + + test('aliased usage before import site', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + bar: BindingTypes.SETUP_REACTIVE_CONST, + x: BindingTypes.SETUP_CONST + }) + }) }) }) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index adc22ab6820..ccf670725ec 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -354,6 +354,25 @@ export function compileScript( ) } + function hoistNode(node: Statement) { + const start = node.start! + startOffset + let end = node.end! + startOffset + // locate comment + if (node.trailingComments && node.trailingComments.length > 0) { + const lastCommentNode = + node.trailingComments[node.trailingComments.length - 1] + end = lastCommentNode.end + startOffset + } + // locate the end of whitespace between this statement and the next + while (end <= source.length) { + if (!/\s/.test(source.charAt(end))) { + break + } + end++ + } + s.move(start, end, 0) + } + function registerUserImport( source: string, local: string, @@ -891,6 +910,7 @@ export function compileScript( scriptStartOffset! ) + // walk import declarations first for (const node of scriptAst.body) { if (node.type === 'ImportDeclaration') { // record imports for dedupe @@ -910,7 +930,11 @@ export function compileScript( !options.inlineTemplate ) } - } else if (node.type === 'ExportDefaultDeclaration') { + } + } + + for (const node of scriptAst.body) { + if (node.type === 'ExportDefaultDeclaration') { // export default defaultExport = node @@ -1040,39 +1064,9 @@ export function compileScript( ) for (const node of scriptSetupAst.body) { - const start = node.start! + startOffset - let end = node.end! + startOffset - // locate comment - if (node.trailingComments && node.trailingComments.length > 0) { - const lastCommentNode = - node.trailingComments[node.trailingComments.length - 1] - end = lastCommentNode.end + startOffset - } - // locate the end of whitespace between this statement and the next - while (end <= source.length) { - if (!/\s/.test(source.charAt(end))) { - break - } - end++ - } - - // (Dropped) `ref: x` bindings - if ( - node.type === 'LabeledStatement' && - node.label.name === 'ref' && - node.body.type === 'ExpressionStatement' - ) { - error( - `ref sugar using the label syntax was an experimental proposal and ` + - `has been dropped based on community feedback. Please check out ` + - `the new proposal at https://github.com/vuejs/rfcs/discussions/369`, - node - ) - } - if (node.type === 'ImportDeclaration') { // import declarations are moved to top - s.move(start, end, 0) + hoistNode(node) // dedupe imports let removed = 0 @@ -1137,6 +1131,26 @@ export function compileScript( s.remove(node.start! + startOffset, node.end! + startOffset) } } + } + + for (const node of scriptSetupAst.body) { + // already processed + if (node.type === 'ImportDeclaration') continue + + // (Dropped) `ref: x` bindings + // TODO remove when out of experimental + if ( + node.type === 'LabeledStatement' && + node.label.name === 'ref' && + node.body.type === 'ExpressionStatement' + ) { + error( + `ref sugar using the label syntax was an experimental proposal and ` + + `has been dropped based on community feedback. Please check out ` + + `the new proposal at https://github.com/vuejs/rfcs/discussions/369`, + node + ) + } if (node.type === 'ExpressionStatement') { // process `defineProps` and `defineEmit(s)` calls @@ -1268,7 +1282,7 @@ export function compileScript( (node.type === 'VariableDeclaration' && node.declare) ) { recordType(node, declaredTypes) - s.move(start, end, 0) + hoistNode(node) } } } From 6861d2380b87566cbc479e065fb67dfeb8c86c46 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 8 Nov 2022 17:47:47 +0800 Subject: [PATCH 50/97] refactor(compiler-sfc): optimize import alias check for binding analysis --- .../__snapshots__/compileScript.spec.ts.snap | 33 +-- .../__tests__/compileScript.spec.ts | 4 +- packages/compiler-sfc/src/compileScript.ts | 214 +++++++++--------- 3 files changed, 127 insertions(+), 124 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index 209bbe1d518..ec94872c560 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -44,9 +44,9 @@ return { a } `; exports[`SFC compile `) @@ -1056,7 +1058,10 @@ const emit = defineEmits(['a', 'b']) `qux: { type: Function, required: false, default() { return 1 } }` ) expect(content).toMatch( - `{ foo: string, bar?: number, baz: boolean, qux(): number }` + `quux: { type: Function, required: false, default() { } }` + ) + expect(content).toMatch( + `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }` ) expect(content).toMatch(`const props = __props`) expect(bindings).toStrictEqual({ @@ -1064,6 +1069,7 @@ const emit = defineEmits(['a', 'b']) bar: BindingTypes.PROPS, baz: BindingTypes.PROPS, qux: BindingTypes.PROPS, + quux: BindingTypes.PROPS, props: BindingTypes.SETUP_CONST }) }) diff --git a/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts index 25fb4bed280..7d35f91abf2 100644 --- a/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts @@ -184,6 +184,24 @@ describe('sfc props transform', () => { assertCode(content) }) + // #6960 + test('computed static key', () => { + const { content, bindings } = compile(` + + + `) + expect(content).not.toMatch(`const { foo } =`) + expect(content).toMatch(`console.log(__props.foo)`) + expect(content).toMatch(`_toDisplayString(__props.foo)`) + assertCode(content) + expect(bindings).toStrictEqual({ + foo: BindingTypes.PROPS + }) + }) + describe('errors', () => { test('should error on deep destructure', () => { expect(() => diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index aba5a22ec8d..d55cb795c77 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -447,18 +447,15 @@ export function compileScript( // props destructure - handle compilation sugar for (const prop of declId.properties) { if (prop.type === 'ObjectProperty') { - if (prop.computed) { + const propKey = resolveObjectKey(prop.key, prop.computed) + + if (!propKey) { error( `${DEFINE_PROPS}() destructure cannot use computed key.`, prop.key ) } - const propKey = - prop.key.type === 'StringLiteral' - ? prop.key.value - : (prop.key as Identifier).name - if (prop.value.type === 'AssignmentPattern') { // default value { foo = 123 } const { left, right } = prop.value @@ -774,7 +771,8 @@ export function compileScript( propsRuntimeDefaults.type === 'ObjectExpression' && propsRuntimeDefaults.properties.every( node => - (node.type === 'ObjectProperty' && !node.computed) || + (node.type === 'ObjectProperty' && + (!node.computed || node.key.type.endsWith('Literal'))) || node.type === 'ObjectMethod' ) ) @@ -795,9 +793,10 @@ export function compileScript( if (destructured) { defaultString = `default: ${destructured}` } else if (hasStaticDefaults) { - const prop = propsRuntimeDefaults!.properties.find( - (node: any) => node.key.name === key - ) as ObjectProperty | ObjectMethod + const prop = propsRuntimeDefaults!.properties.find(node => { + if (node.type === 'SpreadElement') return false + return resolveObjectKey(node.key, node.computed) === key + }) as ObjectProperty | ObjectMethod if (prop) { if (prop.type === 'ObjectProperty') { // prop has corresponding static default value @@ -874,9 +873,13 @@ export function compileScript( m.key.type === 'Identifier' ) { if ( - propsRuntimeDefaults!.properties.some( - (p: any) => p.key.name === (m.key as Identifier).name - ) + propsRuntimeDefaults!.properties.some(p => { + if (p.type === 'SpreadElement') return false + return ( + resolveObjectKey(p.key, p.computed) === + (m.key as Identifier).name + ) + }) ) { res += m.key.name + @@ -2139,16 +2142,9 @@ function analyzeBindingsFromOptions(node: ObjectExpression): BindingMetadata { function getObjectExpressionKeys(node: ObjectExpression): string[] { const keys = [] for (const prop of node.properties) { - if ( - (prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') && - !prop.computed - ) { - if (prop.key.type === 'Identifier') { - keys.push(prop.key.name) - } else if (prop.key.type === 'StringLiteral') { - keys.push(prop.key.value) - } - } + if (prop.type === 'SpreadElement') continue + const key = resolveObjectKey(prop.key, prop.computed) + if (key) keys.push(String(key)) } return keys } @@ -2297,3 +2293,14 @@ export function hmrShouldReload( return false } + +export function resolveObjectKey(node: Node, computed: boolean) { + switch (node.type) { + case 'StringLiteral': + case 'NumericLiteral': + return node.value + case 'Identifier': + if (!computed) return node.name + } + return undefined +} From 5bfe438ef391522bddbe43cd2669061c6a88b03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Wed, 9 Nov 2022 10:58:22 +0800 Subject: [PATCH 58/97] fix(compiler-core/v-on): only apply case preservation on native elements (#6902) fix #6900 --- .../__tests__/transforms/vSlot.spec.ts | 53 ++++++++++++++++++- packages/compiler-core/src/transforms/vOn.ts | 4 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index 93dafe9a25b..c166f8d160a 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -11,7 +11,8 @@ import { VNodeCall, SlotsExpression, ObjectExpression, - SimpleExpressionNode + SimpleExpressionNode, + RenderSlotCall } from '../../src' import { transformElement } from '../../src/transforms/transformElement' import { transformOn } from '../../src/transforms/vOn' @@ -788,6 +789,56 @@ describe('compiler: transform component slots', () => { const { slots } = parseWithSlots(``) expect(slots).toMatchObject(toMatch) }) + + // # fix: #6900 + test('consistent behavior of @xxx:modelValue and @xxx:model-value', () => { + const { root: rootUpper } = parseWithSlots( + `

` + ) + const slotNodeUpper = (rootUpper.codegenNode! as VNodeCall) + .children as ElementNode[] + const propertiesObjUpper = ( + slotNodeUpper[0].codegenNode! as RenderSlotCall + ).arguments[2] + expect(propertiesObjUpper).toMatchObject({ + properties: [ + { + key: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'onFoo:modelValue' + }, + value: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `handler`, + isStatic: false + } + } + ] + }) + + const { root } = parseWithSlots( + `
` + ) + const slotNode = (root.codegenNode! as VNodeCall) + .children as ElementNode[] + const propertiesObj = (slotNode[0].codegenNode! as RenderSlotCall) + .arguments[2] + expect(propertiesObj).toMatchObject({ + properties: [ + { + key: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'onFoo:modelValue' + }, + value: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `handler`, + isStatic: false + } + } + ] + }) + }) }) describe('errors', () => { diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts index b4d2e851ca9..9fe8b6ab61c 100644 --- a/packages/compiler-core/src/transforms/vOn.ts +++ b/packages/compiler-core/src/transforms/vOn.ts @@ -48,10 +48,10 @@ export const transformOn: DirectiveTransform = ( rawName = `vnode-${rawName.slice(4)}` } const eventString = - node.tagType === ElementTypes.COMPONENT || + node.tagType !== ElementTypes.ELEMENT || rawName.startsWith('vnode') || !/[A-Z]/.test(rawName) - ? // for component and vnode lifecycle event listeners, auto convert + ? // for non-element and vnode lifecycle event listeners, auto convert // it to camelCase. See issue #2249 toHandlerKey(camelize(rawName)) : // preserve case for plain element listeners that have uppercase From 8a882ce0a10bfa5482d6b8a9b68fd49cff4f6937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Wed, 9 Nov 2022 11:12:54 +0800 Subject: [PATCH 59/97] fix(compiler-sfc): handle method shorthand syntax in withDefaults (#6972) fix #6971 --- .../__snapshots__/compileScript.spec.ts.snap | 6 ++++-- .../compiler-sfc/__tests__/compileScript.spec.ts | 16 ++++++++++++++-- packages/compiler-sfc/src/compileScript.ts | 4 +++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index 55659cb93d3..d553fb04b3b 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -1744,12 +1744,14 @@ export default /*#__PURE__*/_defineComponent({ bar: { type: Number, required: false }, baz: { type: Boolean, required: true }, qux: { type: Function, required: false, default() { return 1 } }, - quux: { type: Function, required: false, default() { } } + quux: { type: Function, required: false, default() { } }, + quuxx: { type: Promise, required: false, async default() { return await Promise.resolve('hi') } }, + fred: { type: String, required: false, get default() { return 'fred' } } }, setup(__props: any, { expose }) { expose(); -const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }; +const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number, quux(): void, quuxx: Promise, fred: string }; diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index c3ba6067648..bf562defd24 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -1041,10 +1041,14 @@ const emit = defineEmits(['a', 'b']) baz: boolean; qux?(): number; quux?(): void + quuxx?: Promise; + fred?: string }>(), { foo: 'hi', qux() { return 1 }, - ['quux']() { } + ['quux']() { }, + async quuxx() { return await Promise.resolve('hi') }, + get fred() { return 'fred' } }) `) @@ -1061,7 +1065,13 @@ const emit = defineEmits(['a', 'b']) `quux: { type: Function, required: false, default() { } }` ) expect(content).toMatch( - `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }` + `quuxx: { type: Promise, required: false, async default() { return await Promise.resolve('hi') } }` + ) + expect(content).toMatch( + `fred: { type: String, required: false, get default() { return 'fred' } }` + ) + expect(content).toMatch( + `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void, quuxx: Promise, fred: string }` ) expect(content).toMatch(`const props = __props`) expect(bindings).toStrictEqual({ @@ -1070,6 +1080,8 @@ const emit = defineEmits(['a', 'b']) baz: BindingTypes.PROPS, qux: BindingTypes.PROPS, quux: BindingTypes.PROPS, + quuxx: BindingTypes.PROPS, + fred: BindingTypes.PROPS, props: BindingTypes.SETUP_CONST }) }) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index d55cb795c77..e944b764725 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -805,7 +805,9 @@ export function compileScript( prop.value.end! )}` } else { - defaultString = `default() ${scriptSetupSource.slice( + defaultString = `${prop.async ? 'async ' : ''}${ + prop.kind !== 'method' ? `${prop.kind} ` : '' + }default() ${scriptSetupSource.slice( prop.body.start!, prop.body.end! )}` From 2c27556fe5fa8ba991dd55c766a92d3a50fbf8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Wed, 9 Nov 2022 11:22:29 +0800 Subject: [PATCH 60/97] fix(compiler): avoid namespace collisions when transforming template refs in inline mode (#6975) fix #6964 --- .../transforms/transformElement.spec.ts | 26 +++++++++++++++++++ .../src/transforms/transformElement.ts | 26 ++++++++++--------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index 43bd2589df1..06fd2e12b19 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -1063,6 +1063,32 @@ describe('compiler: element transform', () => { }) }) + test('script setup inline mode template ref (binding does not exist but props with the same name exist)', () => { + const { node } = parseWithElementTransform(``, { + inline: true, + bindingMetadata: { + msg: BindingTypes.PROPS, + ref: BindingTypes.SETUP_CONST + } + }) + expect(node.props).toMatchObject({ + type: NodeTypes.JS_OBJECT_EXPRESSION, + properties: [ + { + type: NodeTypes.JS_PROPERTY, + key: { + content: 'ref', + isStatic: true + }, + value: { + content: 'msg', + isStatic: true + } + } + ] + }) + }) + test('HYDRATE_EVENTS', () => { // ignore click events (has dedicated fast path) const { node } = parseWithElementTransform(`
`, { diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index 7b53b24822c..57f84f9f750 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -497,19 +497,21 @@ export function buildProps( // in inline mode there is no setupState object, so we can't use string // keys to set the ref. Instead, we need to transform it to pass the // actual ref instead. - if ( - !__BROWSER__ && - value && - context.inline && - context.bindingMetadata[value.content] - ) { - isStatic = false - properties.push( - createObjectProperty( - createSimpleExpression('ref_key', true), - createSimpleExpression(value.content, true, value.loc) + if (!__BROWSER__ && value && context.inline) { + const binding = context.bindingMetadata[value.content] + if ( + binding === BindingTypes.SETUP_LET || + binding === BindingTypes.SETUP_REF || + binding === BindingTypes.SETUP_MAYBE_REF + ) { + isStatic = false + properties.push( + createObjectProperty( + createSimpleExpression('ref_key', true), + createSimpleExpression(value.content, true, value.loc) + ) ) - ) + } } } // skip is on , or is="vue:xxx" From 57c901383792176fd7267b7d34d845088dbeff63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Wed, 9 Nov 2022 11:30:05 +0800 Subject: [PATCH 61/97] fix(compiler-sfc): only escape parsing-breaking characters in v-bind css var names (#6816) close #6803 --- .../__snapshots__/cssVars.spec.ts.snap | 8 ++++---- .../compiler-sfc/__tests__/cssVars.spec.ts | 20 +++++++++++++------ packages/compiler-sfc/src/cssVars.ts | 6 +++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap index 49b0d712a7a..4df59cc3428 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap @@ -75,9 +75,9 @@ export default { _useCssVars(_ctx => ({ \\"xxxxxxxx-foo\\": (_unref(foo)), - \\"xxxxxxxx-foo____px_\\": (_unref(foo) + 'px'), - \\"xxxxxxxx-_a___b____2____px_\\": ((_unref(a) + _unref(b)) / 2 + 'px'), - \\"xxxxxxxx-__a___b______2___a_\\": (((_unref(a) + _unref(b))) / (2 * _unref(a))) + \\"xxxxxxxx-foo\\\\ \\\\+\\\\ \\\\'px\\\\'\\": (_unref(foo) + 'px'), + \\"xxxxxxxx-\\\\(a\\\\ \\\\+\\\\ b\\\\)\\\\ \\\\/\\\\ 2\\\\ \\\\+\\\\ \\\\'px\\\\'\\": ((_unref(a) + _unref(b)) / 2 + 'px'), + \\"xxxxxxxx-\\\\(\\\\(a\\\\ \\\\+\\\\ b\\\\)\\\\)\\\\ \\\\/\\\\ \\\\(2\\\\ \\\\*\\\\ a\\\\)\\": (((_unref(a) + _unref(b))) / (2 * _unref(a))) })) let a = 100 @@ -133,7 +133,7 @@ import { useCssVars as _useCssVars } from 'vue' const __injectCSSVars__ = () => { _useCssVars(_ctx => ({ \\"xxxxxxxx-color\\": (_ctx.color), - \\"xxxxxxxx-font_size\\": (_ctx.font.size) + \\"xxxxxxxx-font\\\\.size\\": (_ctx.font.size) }))} const __setup__ = __default__.setup __default__.setup = __setup__ diff --git a/packages/compiler-sfc/__tests__/cssVars.spec.ts b/packages/compiler-sfc/__tests__/cssVars.spec.ts index 9d3df069e76..ffa5d4e798b 100644 --- a/packages/compiler-sfc/__tests__/cssVars.spec.ts +++ b/packages/compiler-sfc/__tests__/cssVars.spec.ts @@ -12,7 +12,7 @@ describe('CSS vars injection', () => { ) expect(content).toMatch(`_useCssVars(_ctx => ({ "${mockId}-color": (_ctx.color), - "${mockId}-font_size": (_ctx.font.size) + "${mockId}-font\\.size": (_ctx.font.size) })`) assertCode(content) }) @@ -79,6 +79,10 @@ describe('CSS vars injection', () => { source: `.foo { color: v-bind(color); font-size: v-bind('font.size'); + + font-weight: v-bind(_φ); + font-size: v-bind(1-字号); + font-family: v-bind(フォント); }`, filename: 'test.css', id: 'data-v-test' @@ -86,7 +90,11 @@ describe('CSS vars injection', () => { expect(code).toMatchInlineSnapshot(` ".foo { color: var(--test-color); - font-size: var(--test-font_size); + font-size: var(--test-font\\\\.size); + + font-weight: var(--test-_φ); + font-size: var(--test-1-字号); + font-family: var(--test-フォント); }" `) }) @@ -225,10 +233,10 @@ describe('CSS vars injection', () => { ) expect(content).toMatch(`_useCssVars(_ctx => ({ "${mockId}-foo": (_unref(foo)), - "${mockId}-foo____px_": (_unref(foo) + 'px'), - "${mockId}-_a___b____2____px_": ((_unref(a) + _unref(b)) / 2 + 'px'), - "${mockId}-__a___b______2___a_": (((_unref(a) + _unref(b))) / (2 * _unref(a))) -})`) + "${mockId}-foo\\ \\+\\ \\'px\\'": (_unref(foo) + 'px'), + "${mockId}-\\(a\\ \\+\\ b\\)\\ \\/\\ 2\\ \\+\\ \\'px\\'": ((_unref(a) + _unref(b)) / 2 + 'px'), + "${mockId}-\\(\\(a\\ \\+\\ b\\)\\)\\ \\/\\ \\(2\\ \\*\\ a\\)": (((_unref(a) + _unref(b))) / (2 * _unref(a))) +}))`) assertCode(content) }) diff --git a/packages/compiler-sfc/src/cssVars.ts b/packages/compiler-sfc/src/cssVars.ts index 10f9bb480f1..c7c04f83d72 100644 --- a/packages/compiler-sfc/src/cssVars.ts +++ b/packages/compiler-sfc/src/cssVars.ts @@ -30,7 +30,11 @@ function genVarName(id: string, raw: string, isProd: boolean): string { if (isProd) { return hash(id + raw) } else { - return `${id}-${raw.replace(/([^\w-])/g, '_')}` + // escape ASCII Punctuation & Symbols + return `${id}-${raw.replace( + /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, + s => `\\${s}` + )}` } } From f023d49a4999da5ac822fe47f266b00d9a75f43e Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 9 Nov 2022 11:36:09 +0800 Subject: [PATCH 62/97] release: v3.2.42 --- CHANGELOG.md | 48 +++++++++++++++++++ package.json | 2 +- packages/compiler-core/package.json | 4 +- packages/compiler-dom/package.json | 6 +-- packages/compiler-sfc/package.json | 12 ++--- packages/compiler-ssr/package.json | 6 +-- packages/reactivity-transform/package.json | 6 +-- packages/reactivity/package.json | 4 +- packages/runtime-core/package.json | 6 +-- packages/runtime-dom/package.json | 6 +-- packages/runtime-test/package.json | 6 +-- packages/server-renderer/package.json | 8 ++-- packages/sfc-playground/package.json | 4 +- packages/shared/package.json | 2 +- packages/size-check/package.json | 2 +- packages/template-explorer/package.json | 2 +- packages/vue-compat/package.json | 4 +- packages/vue/package.json | 12 ++--- pnpm-lock.yaml | 54 +++++++++++----------- 19 files changed, 121 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fef732631a..de27f3fa800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,51 @@ +## [3.2.42](https://github.com/vuejs/core/compare/v3.2.41...v3.2.42) (2022-11-09) + + +### Bug Fixes + +* **compiler-core/v-on:** only apply case preservation on native elements ([#6902](https://github.com/vuejs/core/issues/6902)) ([5bfe438](https://github.com/vuejs/core/commit/5bfe438ef391522bddbe43cd2669061c6a88b03a)), closes [#6900](https://github.com/vuejs/core/issues/6900) +* **compiler-core/v-on:** support inline handler with return type annotation ([#6769](https://github.com/vuejs/core/issues/6769)) ([bcfe480](https://github.com/vuejs/core/commit/bcfe480d75d822111c0d3e5fcbc7e10e073d53dc)), closes [#6378](https://github.com/vuejs/core/issues/6378) +* **compiler-core:** avoid duplicate keys in codegen with `v-if` ([#6689](https://github.com/vuejs/core/issues/6689)) ([640cfce](https://github.com/vuejs/core/commit/640cfce4ff808fdfc419058f39a2ff4874a25899)), closes [#6641](https://github.com/vuejs/core/issues/6641) +* **compiler-core:** fix parsing error on comments between v-if in prod ([dd3354c](https://github.com/vuejs/core/commit/dd3354c4c709c0d76e651bb9202158434619cb6a)), closes [#6843](https://github.com/vuejs/core/issues/6843) +* **compiler-core:** keep whitespaces between interpolation and comment ([#6828](https://github.com/vuejs/core/issues/6828)) ([4887618](https://github.com/vuejs/core/commit/48876182dbe5ef88a65a0aa7377e882c735b6104)), closes [#6352](https://github.com/vuejs/core/issues/6352) +* **compiler-sfc:** add semicolon after `defineProps` statement ([#6461](https://github.com/vuejs/core/issues/6461)) ([b72a4af](https://github.com/vuejs/core/commit/b72a4af38a402447d19b4616d09935c390d0702f)), closes [#6428](https://github.com/vuejs/core/issues/6428) +* **compiler-sfc:** allow type annotation for defineEmits variable ([#5394](https://github.com/vuejs/core/issues/5394)) ([eab7604](https://github.com/vuejs/core/commit/eab76046e3b1779dd7a856b6b974e928075d6a1e)), closes [#5393](https://github.com/vuejs/core/issues/5393) +* **compiler-sfc:** check import source during binding analysation ([#6826](https://github.com/vuejs/core/issues/6826)) ([4a00fdd](https://github.com/vuejs/core/commit/4a00fddfed16caee7a1e07756b9c110bc928c17a)), closes [#6825](https://github.com/vuejs/core/issues/6825) +* **compiler-sfc:** fix binding analysis for aliased late import ([8d1f526](https://github.com/vuejs/core/commit/8d1f526174db277ae5aa9297a43f20a43e991294)) +* **compiler-sfc:** fix macro usage in multi-variable declaration ([#6778](https://github.com/vuejs/core/issues/6778)) ([99b6697](https://github.com/vuejs/core/commit/99b6697fb44dd1094ea0bf372c1d05214ffb92a2)), closes [#6757](https://github.com/vuejs/core/issues/6757) +* **compiler-sfc:** handle method shorthand syntax in withDefaults ([#6972](https://github.com/vuejs/core/issues/6972)) ([8a882ce](https://github.com/vuejs/core/commit/8a882ce0a10bfa5482d6b8a9b68fd49cff4f6937)), closes [#6971](https://github.com/vuejs/core/issues/6971) +* **compiler-sfc:** only escape parsing-breaking characters in v-bind css var names ([#6816](https://github.com/vuejs/core/issues/6816)) ([57c9013](https://github.com/vuejs/core/commit/57c901383792176fd7267b7d34d845088dbeff63)), closes [#6803](https://github.com/vuejs/core/issues/6803) +* **compiler-sfc:** require `) - expect(content).toMatch(`return { vMyDir }`) + expect(content).toMatch(`return { get vMyDir() { return vMyDir } }`) assertCode(content) }) @@ -459,7 +464,9 @@ defineExpose({ foo: 123 })
`) - expect(content).toMatch(`return { cond, bar, baz }`) + expect(content).toMatch( + `return { cond, get bar() { return bar }, get baz() { return baz } }` + ) assertCode(content) }) @@ -475,7 +482,9 @@ defineExpose({ foo: 123 }) // x: used in interpolation // y: should not be matched by {{ yy }} or 'y' in binding exps // x$y: #4274 should escape special chars when creating Regex - expect(content).toMatch(`return { x, z, x$y }`) + expect(content).toMatch( + `return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }` + ) assertCode(content) }) @@ -490,7 +499,9 @@ defineExpose({ foo: 123 }) `) // VAR2 should not be matched - expect(content).toMatch(`return { VAR, VAR3 }`) + expect(content).toMatch( + `return { get VAR() { return VAR }, get VAR3() { return VAR3 } }` + ) assertCode(content) }) @@ -505,7 +516,9 @@ defineExpose({ foo: 123 }) `) - expect(content).toMatch(`return { FooBaz, Last }`) + expect(content).toMatch( + `return { get FooBaz() { return FooBaz }, get Last() { return Last } }` + ) assertCode(content) }) @@ -524,7 +537,7 @@ defineExpose({ foo: 123 })
`) - expect(content).toMatch(`return { a, b, Baz }`) + expect(content).toMatch(`return { a, b, get Baz() { return Baz } }`) assertCode(content) }) @@ -1301,7 +1314,7 @@ const emit = defineEmits(['a', 'b']) import { type Bar, Baz } from './main.ts' ` ) - expect(content).toMatch(`return { Baz }`) + expect(content).toMatch(`return { get Baz() { return Baz } }`) assertCode(content) }) }) diff --git a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts index 6d601379749..8ae5275661e 100644 --- a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts @@ -33,7 +33,8 @@ describe('sfc ref transform', () => { expect(content).toMatch(`let c = () => {}`) expect(content).toMatch(`let d`) expect(content).toMatch( - `return { foo, a, b, get c() { return c }, get d() { return d }, ref, shallowRef }` + `return { foo, a, b, get c() { return c }, set c(v) { c = v }, ` + + `get d() { return d }, set d(v) { d = v }, ref, shallowRef }` ) assertCode(content) expect(bindings).toStrictEqual({ diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 74e3dbd7349..7f6b087a268 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -1486,8 +1486,20 @@ export function compileScript( } returned = `{ ` for (const key in allBindings) { - if (bindingMetadata[key] === BindingTypes.SETUP_LET) { + if ( + allBindings[key] === true && + userImports[key].source !== 'vue' && + !userImports[key].source.endsWith('.vue') + ) { + // generate getter for import bindings + // skip vue imports since we know they will never change returned += `get ${key}() { return ${key} }, ` + } else if (bindingMetadata[key] === BindingTypes.SETUP_LET) { + // local let binding, also add setter + const setArg = key === 'v' ? `_v` : `v` + returned += + `get ${key}() { return ${key} }, ` + + `set ${key}(${setArg}) { ${key} = ${setArg} }, ` } else { returned += `${key}, ` } From a54bff2c9c8e1d908b4a0f3826ac715c9a35e68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=B0=94=28Rairn=29?= <958414905@qq.com> Date: Thu, 10 Nov 2022 18:01:31 +0800 Subject: [PATCH 83/97] fix(hmr/keep-alive): fix error in reload component (#7049) fix #7042 --- packages/runtime-core/__tests__/hmr.spec.ts | 67 +++++++++++++++++++ .../runtime-core/src/components/KeepAlive.ts | 4 +- packages/runtime-core/src/vnode.ts | 8 +++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index eaef8d401a7..4b501052ce4 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -151,6 +151,73 @@ describe('hot module replacement', () => { expect(mountSpy).toHaveBeenCalledTimes(1) }) + // #7042 + test('reload KeepAlive slot', async () => { + const root = nodeOps.createElement('div') + const childId = 'test-child-keep-alive' + const unmountSpy = jest.fn() + const mountSpy = jest.fn() + const activeSpy = jest.fn() + const deactiveSpy = jest.fn() + + const Child: ComponentOptions = { + __hmrId: childId, + data() { + return { count: 0 } + }, + unmounted: unmountSpy, + render: compileToFunction(`
{{ count }}
`) + } + createRecord(childId, Child) + + const Parent: ComponentOptions = { + components: { Child }, + data() { + return { toggle: true } + }, + render: compileToFunction( + `` + ) + } + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`
0
`) + + reload(childId, { + __hmrId: childId, + data() { + return { count: 1 } + }, + mounted: mountSpy, + unmounted: unmountSpy, + activated: activeSpy, + deactivated: deactiveSpy, + render: compileToFunction(`
{{ count }}
`) + }) + await nextTick() + expect(serializeInner(root)).toBe(`
1
`) + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(1) + expect(deactiveSpy).toHaveBeenCalledTimes(0) + + // should not unmount when toggling + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(1) + expect(deactiveSpy).toHaveBeenCalledTimes(1) + + // should not mount when toggling + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(2) + expect(deactiveSpy).toHaveBeenCalledTimes(1) + }) + test('reload class component', async () => { const root = nodeOps.createElement('div') const childId = 'test4-child' diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index 9605d79150c..3fec48140fc 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -31,7 +31,6 @@ import { invokeArrayFns } from '@vue/shared' import { watch } from '../apiWatch' -import { hmrDirtyComponents } from '../hmr' import { RendererInternals, queuePostRenderEffect, @@ -281,8 +280,7 @@ const KeepAliveImpl: ComponentOptions = { if ( (include && (!name || !matches(include, name))) || - (exclude && name && matches(exclude, name)) || - (__DEV__ && hmrDirtyComponents.has(comp)) + (exclude && name && matches(exclude, name)) ) { current = vnode return rawVNode diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 7d8017e650a..41f848e44de 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -357,6 +357,14 @@ export function isSameVNodeType(n1: VNode, n2: VNode): boolean { n2.shapeFlag & ShapeFlags.COMPONENT && hmrDirtyComponents.has(n2.type as ConcreteComponent) ) { + // #7042, ensure the vnode being unmounted during HMR + if (n1.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { + n1.shapeFlag -= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + } + // #7042, ensure the vnode being mounted as fresh during HMR + if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { + n2.shapeFlag -= ShapeFlags.COMPONENT_KEPT_ALIVE + } // HMR only: if the component has been hot-updated, force a reload. return false } From 4049ffcf29dc12dca71f682edf0b422a5c502e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=B0=94=28Rairn=29?= <958414905@qq.com> Date: Thu, 10 Nov 2022 18:03:10 +0800 Subject: [PATCH 84/97] fix(runtime-core): fix move/removal of static fragments containing text nodes (#6858) fix #6852 --- .../__tests__/rendererFragment.spec.ts | 36 +++++++++++++++++++ packages/runtime-core/src/renderer.ts | 4 +++ 2 files changed, 40 insertions(+) diff --git a/packages/runtime-core/__tests__/rendererFragment.spec.ts b/packages/runtime-core/__tests__/rendererFragment.spec.ts index 93140f13f4e..1de73ef632c 100644 --- a/packages/runtime-core/__tests__/rendererFragment.spec.ts +++ b/packages/runtime-core/__tests__/rendererFragment.spec.ts @@ -315,4 +315,40 @@ describe('renderer: fragment', () => { `
two
one
` ) }) + + // #6852 + test('`template` keyed fragment w/ text', () => { + const root = nodeOps.createElement('div') + + const renderFn = (items: string[]) => { + return ( + openBlock(true), + createBlock( + Fragment, + null, + renderList(items, item => { + return ( + openBlock(), + createBlock( + Fragment, + { key: item }, + [ + createTextVNode('text'), + createVNode('div', null, item, PatchFlags.TEXT) + ], + PatchFlags.STABLE_FRAGMENT + ) + ) + }), + PatchFlags.KEYED_FRAGMENT + ) + ) + } + + render(renderFn(['one', 'two']), root) + expect(serializeInner(root)).toBe(`text
one
text
two
`) + + render(renderFn(['two', 'one']), root) + expect(serializeInner(root)).toBe(`text
two
text
one
`) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index c043492e1df..4a6d8993a49 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2386,6 +2386,10 @@ export function traverseStaticChildren(n1: VNode, n2: VNode, shallow = false) { } if (!shallow) traverseStaticChildren(c1, c2) } + // #6852 also inherit for text nodes + if (c2.type === Text) { + c2.el = c1.el + } // also inherit for comment nodes, but not placeholders (e.g. v-if which // would have received .el during block patch) if (__DEV__ && c2.type === Comment && !c2.el) { From 845efbbb5d0bc8ba476983d9716668b28a3b8e6f Mon Sep 17 00:00:00 2001 From: Daniel Khalil Date: Fri, 11 Nov 2022 02:12:40 +0100 Subject: [PATCH 85/97] chore: escape html tag in change log (#7089) [ci skip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afcf7169771..7057c13aa60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ * **compiler-sfc:** fix macro usage in multi-variable declaration ([#6778](https://github.com/vuejs/core/issues/6778)) ([99b6697](https://github.com/vuejs/core/commit/99b6697fb44dd1094ea0bf372c1d05214ffb92a2)), closes [#6757](https://github.com/vuejs/core/issues/6757) * **compiler-sfc:** handle method shorthand syntax in withDefaults ([#6972](https://github.com/vuejs/core/issues/6972)) ([8a882ce](https://github.com/vuejs/core/commit/8a882ce0a10bfa5482d6b8a9b68fd49cff4f6937)), closes [#6971](https://github.com/vuejs/core/issues/6971) * **compiler-sfc:** only escape parsing-breaking characters in v-bind css var names ([#6816](https://github.com/vuejs/core/issues/6816)) ([57c9013](https://github.com/vuejs/core/commit/57c901383792176fd7267b7d34d845088dbeff63)), closes [#6803](https://github.com/vuejs/core/issues/6803) -* **compiler-sfc:** require