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.
96 lines
3.1 KiB
96 lines
3.1 KiB
3 years ago
|
/**
|
||
|
* @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'
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|