diff --git a/packages/compiler-core/__tests__/transforms/vFor.spec.ts b/packages/compiler-core/__tests__/transforms/vFor.spec.ts
index 7fabcbb579c..046f822c45a 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(
@@ -1019,5 +1019,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 234cf1fbc30..f5c06b870ea 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,
@@ -41,25 +42,11 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
// same-name shorthand - :arg is expanded to :arg="arg"
if (!exp) {
- if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {
- // only simple expression is allowed for same-name shorthand
- context.onError(
- createCompilerError(
- ErrorCodes.X_V_BIND_INVALID_SAME_NAME_ARGUMENT,
- arg.loc,
- ),
- )
- return {
- props: [
- createObjectProperty(arg, createSimpleExpression('', true, loc)),
- ],
- }
- }
+ if (!exp) {
+ const returned = transformBindShorthand(dir, context)
+ if (returned) return returned
- const propName = camelize((arg as SimpleExpressionNode).content)
- exp = dir.exp = createSimpleExpression(propName, false, arg.loc)
- if (!__BROWSER__) {
- exp = dir.exp = processExpression(exp, context)
+ exp = dir.exp!
}
}
@@ -98,6 +85,32 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
}
}
+export const transformBindShorthand = (
+ dir: DirectiveNode,
+ context: TransformContext,
+) => {
+ const arg = dir.arg!
+ const { loc } = dir
+ if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {
+ // only simple expression is allowed for same-name shorthand
+ context.onError(
+ createCompilerError(
+ ErrorCodes.X_V_BIND_INVALID_SAME_NAME_ARGUMENT,
+ arg.loc,
+ ),
+ )
+ return {
+ props: [createObjectProperty(arg, createSimpleExpression('', true, loc))],
+ }
+ }
+
+ 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..35fe2048724 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) {
+ // try to 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