123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /**
- * @author Toru Nagashima
- * @copyright 2017 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
- 'use strict'
- // ------------------------------------------------------------------------------
- // Requirements
- // ------------------------------------------------------------------------------
- const utils = require('../utils')
- // ------------------------------------------------------------------------------
- // Helpers
- // ------------------------------------------------------------------------------
- const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])
- /**
- * Check whether the given node is valid or not.
- * @param {ASTNode} node The element node to check.
- * @returns {boolean} `true` if the node is valid.
- */
- function isValidElement (node) {
- const name = node.name
- return (
- name === 'input' ||
- name === 'select' ||
- name === 'textarea' ||
- (
- name !== 'keep-alive' &&
- name !== 'slot' &&
- name !== 'transition' &&
- name !== 'transition-group' &&
- utils.isCustomComponent(node)
- )
- )
- }
- /**
- * Check whether the given node can be LHS.
- * @param {ASTNode} node The node to check.
- * @returns {boolean} `true` if the node can be LHS.
- */
- function isLhs (node) {
- return node != null && (
- node.type === 'Identifier' ||
- node.type === 'MemberExpression'
- )
- }
- /**
- * Get the variable by names.
- * @param {string} name The variable name to find.
- * @param {ASTNode} leafNode The node to look up.
- * @returns {Variable|null} The found variable or null.
- */
- function getVariable (name, leafNode) {
- let node = leafNode
- while (node != null) {
- const variables = node.variables
- const variable = variables && variables.find(v => v.id.name === name)
- if (variable != null) {
- return variable
- }
- node = node.parent
- }
- return null
- }
- // ------------------------------------------------------------------------------
- // Rule Definition
- // ------------------------------------------------------------------------------
- module.exports = {
- meta: {
- type: 'problem',
- docs: {
- description: 'enforce valid `v-model` directives',
- category: 'essential',
- url: 'https://eslint.vuejs.org/rules/valid-v-model.html'
- },
- fixable: null,
- schema: []
- },
- create (context) {
- return utils.defineTemplateBodyVisitor(context, {
- "VAttribute[directive=true][key.name='model']" (node) {
- const element = node.parent.parent
- const name = element.name
- if (!isValidElement(element)) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives aren't supported on <{{name}}> elements.",
- data: { name }
- })
- }
- if (name === 'input' && utils.hasAttribute(element, 'type', 'file')) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives don't support 'file' input type."
- })
- }
- if (node.key.argument) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives require no argument."
- })
- }
- for (const modifier of node.key.modifiers) {
- if (!VALID_MODIFIERS.has(modifier)) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives don't support the modifier '{{name}}'.",
- data: { name: modifier }
- })
- }
- }
- if (!utils.hasAttributeValue(node)) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives require that attribute value."
- })
- }
- if (node.value) {
- if (!isLhs(node.value.expression)) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives require the attribute value which is valid as LHS."
- })
- }
- for (const reference of node.value.references) {
- const id = reference.id
- if (id.parent.type !== 'VExpressionContainer') {
- continue
- }
- const variable = getVariable(id.name, element)
- if (variable != null) {
- context.report({
- node,
- loc: node.loc,
- message: "'v-model' directives cannot update the iteration variable '{{varName}}' itself.",
- data: { varName: id.name }
- })
- }
- }
- }
- }
- })
- }
- }
|