diff --git a/lib/rules/valid-v-for.js b/lib/rules/valid-v-for.js index 47d8f5029..8e20f9e66 100644 --- a/lib/rules/valid-v-for.js +++ b/lib/rules/valid-v-for.js @@ -27,7 +27,6 @@ function isUsingIterationVar (vFor, vBindKey) { } const references = vBindKey.value.references const variables = vFor.parent.parent.variables - return references.some(reference => variables.some(variable => variable.id.name === reference.id.name && @@ -36,6 +35,34 @@ function isUsingIterationVar (vFor, vBindKey) { ) } +/** + * Check the child element in tempalte v-for about `v-bind:key` attributes. + * @param {RuleContext} context The rule context to report. + * @param {ASTNode} vFor The attribute node of `v-for` to check. + * @param {ASTNode} child The child node to check. + */ +function checkChildKey (context, vFor, child) { + const childFor = utils.getDirective(child, 'for') + // if child has v-for, check if parent iterator is used in v-for + if (childFor != null) { + const childForRefs = childFor.value.references + const variables = vFor.parent.parent.variables + const usedInFor = childForRefs.some(cref => + variables.some(variable => + cref.id.name === variable.id.name && + variable.kind === 'v-for' + ) + ) + // if parent iterator is used, skip other checks + // iterator usage will be checked later by child v-for + if (usedInFor) { + return + } + } + // otherwise, check if parent iterator is directly used in child's key + checkKey(context, vFor, child) +} + /** * Check the given element about `v-bind:key` attributes. * @param {RuleContext} context The rule context to report. @@ -46,7 +73,7 @@ function checkKey (context, vFor, element) { if (element.name === 'template') { for (const child of element.children) { if (child.type === 'VElement') { - checkKey(context, vFor, child) + checkChildKey(context, vFor, child) } } return diff --git a/tests/lib/rules/valid-v-for.js b/tests/lib/rules/valid-v-for.js index e3da7c5ed..6fe1f0f1c 100644 --- a/tests/lib/rules/valid-v-for.js +++ b/tests/lib/rules/valid-v-for.js @@ -82,6 +82,32 @@ tester.run('valid-v-for', rule, { { filename: 'test.vue', code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: ` + ` + }, + { + filename: 'test.vue', + code: ` + ` } ], invalid: [ @@ -179,6 +205,48 @@ tester.run('valid-v-for', rule, { filename: 'test.vue', code: '', errors: ["'v-for' directives require that attribute value."] + }, + { + filename: 'test.vue', + code: '', + errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + }, + { + filename: 'test.vue', + errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."], + code: ` + ` + }, + { + filename: 'test.vue', + errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."], + code: ` + ` + }, + { + filename: 'test.vue', + errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."], + code: ` + ` } + ] })