You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
3.1 KiB
95 lines
3.1 KiB
/** |
|
* @author Yosuke Ota |
|
* This rule is based on X_V_FOR_TEMPLATE_KEY_PLACEMENT error of Vue 3. |
|
* see https://github.com/vuejs/vue-next/blob/b0d01e9db9ffe5781cce5a2d62c8552db3d615b0/packages/compiler-core/src/errors.ts#L70 |
|
*/ |
|
'use strict' |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Requirements |
|
// ------------------------------------------------------------------------------ |
|
|
|
const utils = require('../utils') |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Helpers |
|
// ------------------------------------------------------------------------------ |
|
|
|
/** |
|
* Check whether the given attribute is using the variables which are defined by `v-for` directives. |
|
* @param {VDirective} vFor The attribute node of `v-for` to check. |
|
* @param {VDirective} 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(vFor, vBindKey) { |
|
if (vBindKey.value == null) { |
|
return false |
|
} |
|
const references = vBindKey.value.references |
|
const variables = vFor.parent.parent.variables |
|
return references.some((reference) => |
|
variables.some( |
|
(variable) => |
|
variable.id.name === reference.id.name && variable.kind === 'v-for' |
|
) |
|
) |
|
} |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Rule Definition |
|
// ------------------------------------------------------------------------------ |
|
|
|
module.exports = { |
|
meta: { |
|
type: 'problem', |
|
docs: { |
|
description: |
|
'disallow key of `<template v-for>` placed on child elements', |
|
categories: ['vue3-essential'], |
|
url: 'https://eslint.vuejs.org/rules/no-v-for-template-key-on-child.html' |
|
}, |
|
fixable: null, |
|
schema: [], |
|
messages: { |
|
vForTemplateKeyPlacement: |
|
'`<template v-for>` key should be placed on the `<template>` tag.' |
|
} |
|
}, |
|
/** @param {RuleContext} context */ |
|
create(context) { |
|
return utils.defineTemplateBodyVisitor(context, { |
|
/** @param {VDirective} node */ |
|
"VElement[name='template'] > VStartTag > VAttribute[directive=true][key.name.name='for']"( |
|
node |
|
) { |
|
const template = node.parent.parent |
|
const vBindKeyOnTemplate = utils.getDirective(template, 'bind', 'key') |
|
if ( |
|
vBindKeyOnTemplate && |
|
isUsingIterationVar(node, vBindKeyOnTemplate) |
|
) { |
|
return |
|
} |
|
|
|
for (const child of template.children.filter(utils.isVElement)) { |
|
if ( |
|
utils.hasDirective(child, 'if') || |
|
utils.hasDirective(child, 'else-if') || |
|
utils.hasDirective(child, 'else') || |
|
utils.hasDirective(child, 'for') |
|
) { |
|
continue |
|
} |
|
const vBindKeyOnChild = utils.getDirective(child, 'bind', 'key') |
|
if (vBindKeyOnChild && isUsingIterationVar(node, vBindKeyOnChild)) { |
|
context.report({ |
|
node: vBindKeyOnChild, |
|
loc: vBindKeyOnChild.loc, |
|
messageId: 'vForTemplateKeyPlacement' |
|
}) |
|
} |
|
} |
|
} |
|
}) |
|
} |
|
}
|
|
|