})
)
})
+
+ test('<template v-for> key placement', () => {
+ const onError = jest.fn()
+ parseWithForTransform(
+ `
+ <template v-for="item in items">
+ <div :key="item.id"/>
+ </template>`,
+ { onError }
+ )
+
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: ErrorCodes.X_V_FOR_TEMPLATE_KEY_PLACEMENT
+ })
+ )
+
+ // should not warn on nested v-for keys
+ parseWithForTransform(
+ `
+ <template v-for="item in items">
+ <div v-for="c in item.children" :key="c.id"/>
+ </template>`,
+ { onError }
+ )
+ expect(onError).toHaveBeenCalledTimes(1)
+ })
})
describe('source location', () => {
X_V_ELSE_NO_ADJACENT_IF,
X_V_FOR_NO_EXPRESSION,
X_V_FOR_MALFORMED_EXPRESSION,
+ X_V_FOR_TEMPLATE_KEY_PLACEMENT,
X_V_BIND_NO_EXPRESSION,
X_V_ON_NO_EXPRESSION,
X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
[ErrorCodes.X_V_ELSE_NO_ADJACENT_IF]: `v-else/v-else-if has no adjacent v-if.`,
[ErrorCodes.X_V_FOR_NO_EXPRESSION]: `v-for is missing expression.`,
[ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION]: `v-for has invalid expression.`,
+ [ErrorCodes.X_V_FOR_TEMPLATE_KEY_PLACEMENT]: `<template v-for> key should be placed on the <template> tag.`,
[ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind is missing expression.`,
[ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression.`,
[ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET]: `Unexpected custom directive on <slot> outlet.`,
SimpleExpressionNode,
createCallExpression,
createFunctionExpression,
- ElementTypes,
createObjectExpression,
createObjectProperty,
ForCodegenNode,
let childBlock: BlockCodegenNode
const isTemplate = isTemplateNode(node)
const { children } = forNode
+
+ // check <template v-for> key placement
+ if ((__DEV__ || !__BROWSER__) && isTemplate) {
+ node.children.some(c => {
+ if (c.type === NodeTypes.ELEMENT) {
+ const key = findProp(c, 'key')
+ if (key) {
+ context.onError(
+ createCompilerError(
+ ErrorCodes.X_V_FOR_TEMPLATE_KEY_PLACEMENT,
+ key.loc
+ )
+ )
+ return true
+ }
+ }
+ })
+ }
+
const needFragmentWrapper =
children.length !== 1 || children[0].type !== NodeTypes.ELEMENT
const slotOutlet = isSlotOutlet(node)
keyAlias: key,
objectIndexAlias: index,
parseResult,
- children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
+ children: isTemplateNode(node) ? node.children : [node]
}
context.replaceNode(forNode)