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 e434b8888..94f75f2a6 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(
+ 'test
',
+ )
+ 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 234cf1fbc..cec444a5a 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 5d423ee24..16c48ede0 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