From 29425df1acb9e520c6ae894d06bcff73fde90edd Mon Sep 17 00:00:00 2001 From: Vadim Kruglov <49036220+quiteeasy@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:18:24 +0700 Subject: [PATCH] fix(compiler-core): fix :key shorthand on v-for (#10942) close #10882 close #10939 --- .../__tests__/transforms/vFor.spec.ts | 30 ++++++++++++++++++- .../compiler-core/src/transforms/vBind.ts | 23 ++++++++++---- packages/compiler-core/src/transforms/vFor.ts | 16 +++++++--- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/vFor.spec.ts b/packages/compiler-core/__tests__/transforms/vFor.spec.ts index e434b8888a6..94f75f2a63b 100644 --- a/packages/compiler-core/__tests__/transforms/vFor.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vFor.spec.ts @@ -18,7 +18,7 @@ import { import { ErrorCodes } from '../../src/errors' import { type CompilerOptions, generate } from '../../src' import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers' -import { PatchFlags } from '@vue/shared' +import { PatchFlagNames, PatchFlags } from '@vue/shared' import { createObjectMatcher, genFlagText } from '../testUtils' export function parseWithForTransform( @@ -1043,5 +1043,33 @@ describe('compiler: v-for', () => { }) expect(generate(root).code).toMatchSnapshot() }) + + test('template v-for key w/ :key shorthand on div', () => { + const { + node: { codegenNode }, + } = parseWithForTransform('
test
') + expect(codegenNode.patchFlag).toBe( + `${PatchFlags.KEYED_FRAGMENT} /* ${PatchFlagNames[PatchFlags.KEYED_FRAGMENT]} */`, + ) + }) + + test('template v-for key w/ :key shorthand on template injected to the child', () => { + const { + node: { codegenNode }, + } = parseWithForTransform( + '', + ) + expect(assertSharedCodegen(codegenNode, true)).toMatchObject({ + source: { content: `keys` }, + params: [{ content: `key` }], + innerVNodeCall: { + type: NodeTypes.VNODE_CALL, + tag: `"div"`, + props: createObjectMatcher({ + key: '[key]', + }), + }, + }) + }) }) }) diff --git a/packages/compiler-core/src/transforms/vBind.ts b/packages/compiler-core/src/transforms/vBind.ts index 234cf1fbc30..cec444a5a1a 100644 --- a/packages/compiler-core/src/transforms/vBind.ts +++ b/packages/compiler-core/src/transforms/vBind.ts @@ -1,5 +1,6 @@ -import type { DirectiveTransform } from '../transform' +import type { DirectiveTransform, TransformContext } from '../transform' import { + type DirectiveNode, type ExpressionNode, NodeTypes, type SimpleExpressionNode, @@ -56,11 +57,8 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => { } } - const propName = camelize((arg as SimpleExpressionNode).content) - exp = dir.exp = createSimpleExpression(propName, false, arg.loc) - if (!__BROWSER__) { - exp = dir.exp = processExpression(exp, context) - } + transformBindShorthand(dir, context) + exp = dir.exp! } if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) { @@ -98,6 +96,19 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => { } } +export const transformBindShorthand = ( + dir: DirectiveNode, + context: TransformContext, +) => { + const arg = dir.arg! + + const propName = camelize((arg as SimpleExpressionNode).content) + dir.exp = createSimpleExpression(propName, false, arg.loc) + if (!__BROWSER__) { + dir.exp = processExpression(dir.exp, context) + } +} + const injectPrefix = (arg: ExpressionNode, prefix: string) => { if (arg.type === NodeTypes.SIMPLE_EXPRESSION) { if (arg.isStatic) { diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index 5d423ee2429..16c48ede067 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -47,6 +47,7 @@ import { import { processExpression } from './transformExpression' import { validateBrowserExpression } from '../validateExpression' import { PatchFlagNames, PatchFlags } from '@vue/shared' +import { transformBindShorthand } from './vBind' export const transformFor = createStructuralDirectiveTransform( 'for', @@ -60,13 +61,20 @@ export const transformFor = createStructuralDirectiveTransform( ]) as ForRenderListExpression const isTemplate = isTemplateNode(node) const memo = findDir(node, 'memo') - const keyProp = findProp(node, `key`) + const keyProp = findProp(node, `key`, false, true) + if (keyProp && keyProp.type === NodeTypes.DIRECTIVE && !keyProp.exp) { + // resolve :key shorthand #10882 + transformBindShorthand(keyProp, context) + } const keyExp = keyProp && (keyProp.type === NodeTypes.ATTRIBUTE - ? createSimpleExpression(keyProp.value!.content, true) - : keyProp.exp!) - const keyProperty = keyProp ? createObjectProperty(`key`, keyExp!) : null + ? keyProp.value + ? createSimpleExpression(keyProp.value.content, true) + : undefined + : keyProp.exp) + const keyProperty = + keyProp && keyExp ? createObjectProperty(`key`, keyExp) : null if (!__BROWSER__ && isTemplate) { // #2085 / #5288 process :key and v-memo expressions need to be