Skip to content

Commit 4cc3c6c

Browse files
committed
Update: add check for key attribute of child of templates (refs #43)
1 parent a800d96 commit 4cc3c6c

File tree

4 files changed

+99
-31
lines changed

4 files changed

+99
-31
lines changed

lib/rules/no-invalid-v-for.js

+41-19
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ const utils = require('../utils')
1717

1818
/**
1919
* Check whether the given attribute is using the variables which are defined by `v-for` directives.
20-
* @param {ASTNode} node The attribute node to check.
20+
* @param {ASTNode} vFor The attribute node of `v-for` to check.
21+
* @param {ASTNode} vBindKey The attribute node of `v-bind:key` to check.
2122
* @returns {boolean} `true` if the node is using the variables which are defined by `v-for` directives.
2223
*/
23-
function isUsingIterationVar (node) {
24-
if (node.value == null) {
24+
function isUsingIterationVar (vFor, vBindKey) {
25+
if (vBindKey.value == null) {
2526
return false
2627
}
27-
const references = node.value.references
28-
const variables = node.parent.parent.variables
28+
const references = vBindKey.value.references
29+
const variables = vFor.parent.parent.variables
2930

3031
return references.some(reference =>
3132
variables.some(variable =>
@@ -35,6 +36,32 @@ function isUsingIterationVar (node) {
3536
)
3637
}
3738

39+
/**
40+
* Check the given element about `v-bind:key` attributes.
41+
* @param {RuleContext} context The rule context to report.
42+
* @param {ASTNode} vFor The attribute node of `v-for` to check.
43+
* @param {ASTNode} element The element node to check.
44+
*/
45+
function checkKey (context, vFor, element) {
46+
const startTag = element.startTag
47+
const vBindKey = utils.getDirective(startTag, 'bind', 'key')
48+
49+
if (utils.isCustomComponent(startTag) && vBindKey == null) {
50+
context.report({
51+
node: startTag,
52+
loc: startTag.loc,
53+
message: "Custom elements in iteration require 'v-bind:key' directives."
54+
})
55+
}
56+
if (vBindKey != null && !isUsingIterationVar(vFor, vBindKey)) {
57+
context.report({
58+
node: vBindKey,
59+
loc: vBindKey.loc,
60+
message: "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."
61+
})
62+
}
63+
}
64+
3865
/**
3966
* Creates AST event handlers for no-invalid-v-for.
4067
*
@@ -46,20 +73,15 @@ function create (context) {
4673

4774
utils.registerTemplateBodyVisitor(context, {
4875
"VAttribute[directive=true][key.name='for']" (node) {
49-
const vBindKey = utils.getDirective(node.parent, 'bind', 'key')
50-
if (utils.isCustomComponent(node.parent) && vBindKey == null) {
51-
context.report({
52-
node,
53-
loc: node.loc,
54-
message: "'v-for' directives on custom elements require 'v-bind:key' directives."
55-
})
56-
}
57-
if (vBindKey != null && !isUsingIterationVar(vBindKey)) {
58-
context.report({
59-
node: vBindKey,
60-
loc: vBindKey.loc,
61-
message: "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."
62-
})
76+
const element = node.parent.parent
77+
78+
checkKey(context, node, element)
79+
if (element.startTag.id.name === 'template') {
80+
for (const child of element.children) {
81+
if (child.type === 'VElement') {
82+
checkKey(context, node, child)
83+
}
84+
}
6385
}
6486

6587
if (node.key.argument) {

lib/rules/require-v-for-key.js

+26-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ const utils = require('../utils')
1515
// Helpers
1616
// ------------------------------------------------------------------------------
1717

18+
/**
19+
* Check the given element about `v-bind:key` attributes.
20+
* @param {RuleContext} context The rule context to report.
21+
* @param {ASTNode} element The element node to check.
22+
*/
23+
function checkKey (context, element) {
24+
const startTag = element.startTag
25+
26+
if (startTag.id.name !== 'template' && !utils.isCustomComponent(startTag) && !utils.hasDirective(startTag, 'bind', 'key')) {
27+
context.report({
28+
node: startTag,
29+
loc: startTag.loc,
30+
message: "Elements in iteration expect to have 'v-bind:key' directives."
31+
})
32+
}
33+
}
34+
1835
/**
1936
* Creates AST event handlers for require-v-for-key.
2037
*
@@ -24,12 +41,15 @@ const utils = require('../utils')
2441
function create (context) {
2542
utils.registerTemplateBodyVisitor(context, {
2643
"VAttribute[directive=true][key.name='for']" (node) {
27-
if (!utils.hasDirective(node.parent, 'bind', 'key') && !utils.isCustomComponent(node.parent)) {
28-
context.report({
29-
node: node.parent,
30-
loc: node.parent.loc,
31-
message: "'v-for' directives require 'v-bind:key' directives."
32-
})
44+
const element = node.parent.parent
45+
46+
checkKey(context, element)
47+
if (element.startTag.id.name === 'template') {
48+
for (const child of element.children) {
49+
if (child.type === 'VElement') {
50+
checkKey(context, child)
51+
}
52+
}
3353
}
3454
}
3555
})

tests/lib/rules/no-invalid-v-for.js

+21-4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ tester.run('no-invalid-v-for', rule, {
6262
{
6363
filename: 'test.vue',
6464
code: '<template><div><div :is="your-component" v-for="x in list" :key="x.id"></div></div></template>'
65+
},
66+
{
67+
filename: 'test.vue',
68+
code: '<template><div><template v-for="x in list"><custom-component :key="x"></custom-component></template></div></template>'
69+
},
70+
{
71+
filename: 'test.vue',
72+
code: '<template><div><template v-for="x in list"><div :key="x"></div></template></div></template>'
73+
},
74+
{
75+
filename: 'test.vue',
76+
code: '<template><div><template v-for="x in list"><div></div></template></div></template>'
6577
}
6678
],
6779
invalid: [
@@ -108,22 +120,22 @@ tester.run('no-invalid-v-for', rule, {
108120
{
109121
filename: 'test.vue',
110122
code: '<template><div><your-component v-for="x in list"></your-component></div></template>',
111-
errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."]
123+
errors: ["Custom elements in iteration require 'v-bind:key' directives."]
112124
},
113125
{
114126
filename: 'test.vue',
115127
code: '<template><div><div is="your-component" v-for="x in list"></div></div></template>',
116-
errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."]
128+
errors: ["Custom elements in iteration require 'v-bind:key' directives."]
117129
},
118130
{
119131
filename: 'test.vue',
120132
code: '<template><div><div :is="your-component" v-for="x in list"></div></div></template>',
121-
errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."]
133+
errors: ["Custom elements in iteration require 'v-bind:key' directives."]
122134
},
123135
{
124136
filename: 'test.vue',
125137
code: '<template><div><div v-bind:is="your-component" v-for="x in list"></div></div></template>',
126-
errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."]
138+
errors: ["Custom elements in iteration require 'v-bind:key' directives."]
127139
},
128140
{
129141
filename: 'test.vue',
@@ -149,6 +161,11 @@ tester.run('no-invalid-v-for', rule, {
149161
filename: 'test.vue',
150162
code: '<template><div><div v-for="(item, index) in suggestions" :key></div></div></template>',
151163
errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."]
164+
},
165+
{
166+
filename: 'test.vue',
167+
code: '<template><div><template v-for="x in list" :key="x"><custom-component></custom-component></template></div></template>',
168+
errors: ["Custom elements in iteration require 'v-bind:key' directives."]
152169
}
153170
]
154171
})

tests/lib/rules/require-v-for-key.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,27 @@ tester.run('require-v-for-key', rule, {
3838
{
3939
filename: 'test.vue',
4040
code: '<template><div><custom-component v-for="x in list"></custom-component></div></template>'
41+
},
42+
{
43+
filename: 'test.vue',
44+
code: '<template><div><template v-for="x in list"><div :key="x"></div></template></div></template>'
4145
}
4246
],
4347
invalid: [
4448
{
4549
filename: 'test.vue',
4650
code: '<template><div><div v-for="x in list"></div></div></template>',
47-
errors: ["'v-for' directives require 'v-bind:key' directives."]
51+
errors: ["Elements in iteration expect to have 'v-bind:key' directives."]
4852
},
4953
{
5054
filename: 'test.vue',
5155
code: '<template><div><div v-for="x in list" key="100"></div></div></template>',
52-
errors: ["'v-for' directives require 'v-bind:key' directives."]
56+
errors: ["Elements in iteration expect to have 'v-bind:key' directives."]
57+
},
58+
{
59+
filename: 'test.vue',
60+
code: '<template><div><template v-for="x in list"><div></div></template></div></template>',
61+
errors: ["Elements in iteration expect to have 'v-bind:key' directives."]
5362
}
5463
]
5564
})

0 commit comments

Comments
 (0)