From d1d43b467032442487892538e03b64e38ef382c1 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 22 Jun 2017 21:42:26 +0900 Subject: [PATCH 1/3] Update: add check for `key` attribute of child of templates (refs #43) --- lib/rules/no-invalid-v-for.js | 60 +++++++++++++++++++--------- lib/rules/require-v-for-key.js | 32 ++++++++++++--- tests/lib/rules/no-invalid-v-for.js | 25 ++++++++++-- tests/lib/rules/require-v-for-key.js | 13 +++++- 4 files changed, 99 insertions(+), 31 deletions(-) diff --git a/lib/rules/no-invalid-v-for.js b/lib/rules/no-invalid-v-for.js index 9b19599a3..0ebe86ad0 100644 --- a/lib/rules/no-invalid-v-for.js +++ b/lib/rules/no-invalid-v-for.js @@ -17,15 +17,16 @@ const utils = require('../utils') /** * Check whether the given attribute is using the variables which are defined by `v-for` directives. - * @param {ASTNode} node The attribute node to check. + * @param {ASTNode} vFor The attribute node of `v-for` to check. + * @param {ASTNode} vBindKey The attribute node of `v-bind:key` to check. * @returns {boolean} `true` if the node is using the variables which are defined by `v-for` directives. */ -function isUsingIterationVar (node) { - if (node.value == null) { +function isUsingIterationVar (vFor, vBindKey) { + if (vBindKey.value == null) { return false } - const references = node.value.references - const variables = node.parent.parent.variables + const references = vBindKey.value.references + const variables = vFor.parent.parent.variables return references.some(reference => variables.some(variable => @@ -35,6 +36,32 @@ function isUsingIterationVar (node) { ) } +/** + * Check the given element 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} element The element node to check. + */ +function checkKey (context, vFor, element) { + const startTag = element.startTag + const vBindKey = utils.getDirective(startTag, 'bind', 'key') + + if (utils.isCustomComponent(startTag) && vBindKey == null) { + context.report({ + node: startTag, + loc: startTag.loc, + message: "Custom elements in iteration require 'v-bind:key' directives." + }) + } + if (vBindKey != null && !isUsingIterationVar(vFor, vBindKey)) { + context.report({ + node: vBindKey, + loc: vBindKey.loc, + message: "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + }) + } +} + /** * Creates AST event handlers for no-invalid-v-for. * @@ -46,20 +73,15 @@ function create (context) { utils.registerTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name='for']" (node) { - const vBindKey = utils.getDirective(node.parent, 'bind', 'key') - if (utils.isCustomComponent(node.parent) && vBindKey == null) { - context.report({ - node, - loc: node.loc, - message: "'v-for' directives on custom elements require 'v-bind:key' directives." - }) - } - if (vBindKey != null && !isUsingIterationVar(vBindKey)) { - context.report({ - node: vBindKey, - loc: vBindKey.loc, - message: "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." - }) + const element = node.parent.parent + + checkKey(context, node, element) + if (element.startTag.id.name === 'template') { + for (const child of element.children) { + if (child.type === 'VElement') { + checkKey(context, node, child) + } + } } if (node.key.argument) { diff --git a/lib/rules/require-v-for-key.js b/lib/rules/require-v-for-key.js index a489c45c9..cc4599e6e 100644 --- a/lib/rules/require-v-for-key.js +++ b/lib/rules/require-v-for-key.js @@ -15,6 +15,23 @@ const utils = require('../utils') // Helpers // ------------------------------------------------------------------------------ +/** + * Check the given element about `v-bind:key` attributes. + * @param {RuleContext} context The rule context to report. + * @param {ASTNode} element The element node to check. + */ +function checkKey (context, element) { + const startTag = element.startTag + + if (startTag.id.name !== 'template' && !utils.isCustomComponent(startTag) && !utils.hasDirective(startTag, 'bind', 'key')) { + context.report({ + node: startTag, + loc: startTag.loc, + message: "Elements in iteration expect to have 'v-bind:key' directives." + }) + } +} + /** * Creates AST event handlers for require-v-for-key. * @@ -24,12 +41,15 @@ const utils = require('../utils') function create (context) { utils.registerTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name='for']" (node) { - if (!utils.hasDirective(node.parent, 'bind', 'key') && !utils.isCustomComponent(node.parent)) { - context.report({ - node: node.parent, - loc: node.parent.loc, - message: "'v-for' directives require 'v-bind:key' directives." - }) + const element = node.parent.parent + + checkKey(context, element) + if (element.startTag.id.name === 'template') { + for (const child of element.children) { + if (child.type === 'VElement') { + checkKey(context, child) + } + } } } }) diff --git a/tests/lib/rules/no-invalid-v-for.js b/tests/lib/rules/no-invalid-v-for.js index f465b567c..24c739689 100644 --- a/tests/lib/rules/no-invalid-v-for.js +++ b/tests/lib/rules/no-invalid-v-for.js @@ -62,6 +62,18 @@ tester.run('no-invalid-v-for', rule, { { filename: 'test.vue', code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' } ], invalid: [ @@ -108,22 +120,22 @@ tester.run('no-invalid-v-for', rule, { { filename: 'test.vue', code: '', - errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."] + errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', code: '', - errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."] + errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', code: '', - errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."] + errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', code: '', - errors: ["'v-for' directives on custom elements require 'v-bind:key' directives."] + errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', @@ -149,6 +161,11 @@ tester.run('no-invalid-v-for', rule, { 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', + code: '', + errors: ["Custom elements in iteration require 'v-bind:key' directives."] } ] }) diff --git a/tests/lib/rules/require-v-for-key.js b/tests/lib/rules/require-v-for-key.js index 156e4befa..5d4a18376 100644 --- a/tests/lib/rules/require-v-for-key.js +++ b/tests/lib/rules/require-v-for-key.js @@ -38,18 +38,27 @@ tester.run('require-v-for-key', rule, { { filename: 'test.vue', code: '' + }, + { + filename: 'test.vue', + code: '' } ], invalid: [ { filename: 'test.vue', code: '', - errors: ["'v-for' directives require 'v-bind:key' directives."] + errors: ["Elements in iteration expect to have 'v-bind:key' directives."] }, { filename: 'test.vue', code: '', - errors: ["'v-for' directives require 'v-bind:key' directives."] + errors: ["Elements in iteration expect to have 'v-bind:key' directives."] + }, + { + filename: 'test.vue', + code: '', + errors: ["Elements in iteration expect to have 'v-bind:key' directives."] } ] }) From 0a775eff57c28078f1bc3e6244d7aae73665ec44 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 22 Jun 2017 21:43:15 +0900 Subject: [PATCH 2/3] New: no-template-key (fixes #43) --- README.md | 1 + docs/rules/no-template-key.md | 34 ++++++++++++++++ lib/recommended-rules.js | 1 + lib/rules/no-template-key.js | 55 +++++++++++++++++++++++++ tests/lib/rules/no-template-key.js | 64 ++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 docs/rules/no-template-key.md create mode 100644 lib/rules/no-template-key.js create mode 100644 tests/lib/rules/no-template-key.js diff --git a/README.md b/README.md index cea828509..befce8d0d 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ The `--fix` option on the command line automatically fixes problems reported by | :white_check_mark: | [no-invalid-v-show](./docs/rules/no-invalid-v-show.md) | disallow invalid v-show directives. | | :white_check_mark: | [no-invalid-v-text](./docs/rules/no-invalid-v-text.md) | disallow invalid v-text directives. | | :white_check_mark: | [no-parsing-error](./docs/rules/no-parsing-error.md) | disallow parsing errors in `