code:
/**
 * @author Toru Nagashima
 * See LICENSE file in root directory for full license.
 */
'use strict'

const { pascalCase } = require('../utils/casing')
const utils = require('../utils')

/**
 * @typedef {Object} Options
 * @property {"shorthand" | "longform" | "v-slot"} atComponent The style for the default slot at a custom component directly.
 * @property {"shorthand" | "longform" | "v-slot"} default The style for the default slot at a template wrapper.
 * @property {"shorthand" | "longform"} named The style for named slots at a template wrapper.
 */

/**
 * Normalize options.
 * @param {any} options The raw options to normalize.
 * @returns {Options} The normalized options.
 */
function normalizeOptions(options) {
  /** @type {Options} */
  const normalized = {
    atComponent: 'v-slot',
    default: 'shorthand',
    named: 'shorthand'
  }

  if (typeof options === 'string') {
    normalized.atComponent =
      normalized.default =
      normalized.named =
        /** @type {"shorthand" | "longform"} */ (options)
  } else if (options != null) {
    /** @type {(keyof Options)[]} */
    const keys = ['atComponent', 'default', 'named']
    for (const key of keys) {
      if (options[key] != null) {
        normalized[key] = options[key]
      }
    }
  }

  return normalized
}

/**
 * Get the expected style.
 * @param {Options} options The options that defined expected types.
 * @param {VDirective} node The `v-slot` node to check.
 * @returns {"shorthand" | "longform" | "v-slot"} The expected style.
 */
function getExpectedStyle(options, node) {
  const { argument } = node.key

  if (
    argument == null ||
    (argument.type === 'VIdentifier' && argument.name === 'default')
  ) {
    const element = node.parent.parent
    return element.name === 'template' ? options.default : options.atComponent
  }
  return options.named
}

/**
 * Get the expected style.
 * @param {VDirective} node The `v-slot` node to check.
 * @returns {"shorthand" | "longform" | "v-slot"} The expected style.
 */
function getActualStyle(node) {
  const { name, argument } = node.key

  if (name.rawName === '#') {
    return 'shorthand'
  }
  if (argument != null) {
    return 'longform'
  }
  return 'v-slot'
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'enforce `v-slot` directive style',
      categories: ['vue3-strongly-recommended', 'vue2-strongly-recommended'],
      url: 'https://eslint.vuejs.org/rules/v-slot-style.html'
    },
    fixable: 'code',
    schema: [
      {
        anyOf: [
          { enum: ['shorthand', 'longform'] },
          {
            type: 'object',
            properties: {
              atComponent: { enum: ['shorthand', 'longform', 'v-slot'] },
              default: { enum: ['shorthand', 'longform', 'v-slot'] },
              named: { enum: ['shorthand', 'longform'] }
            },
            additionalProperties: false
          }
        ]
      }
    ],
    messages: {
      expectedShorthand: "Expected '#{{argument}}' instead of '{{actual}}'.",
      expectedLongform:
        "Expected 'v-slot:{{argument}}' instead of '{{actual}}'.",
      expectedVSlot: "Expected 'v-slot' instead of '{{actual}}'."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const sourceCode = context.getSourceCode()
    const options = normalizeOptions(context.options[0])

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='slot']"(node) {
        const expected = getExpectedStyle(options, node)
        const actual = getActualStyle(node)
        if (actual === expected) {
          return
        }

        const { name, argument } = node.key
        /** @type {Range} */
        const range = [name.range[0], (argument || name).range[1]]
        const argumentText = argument ? sourceCode.getText(argument) : 'default'
        context.report({
          node,
          messageId: `expected${pascalCase(expected)}`,
          data: {
            actual: sourceCode.text.slice(range[0], range[1]),
            argument: argumentText
          },

          fix(fixer) {
            switch (expected) {
              case 'shorthand': {
                return fixer.replaceTextRange(range, `#${argumentText}`)
              }
              case 'longform': {
                return fixer.replaceTextRange(range, `v-slot:${argumentText}`)
              }
              case 'v-slot': {
                return fixer.replaceTextRange(range, 'v-slot')
              }
              default: {
                return null
              }
            }
          }
        })
      }
    })
  }
}


code:
/**
 * @author Doug Wade <douglas.b.wade@gmail.com>
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')
const xnv = require('xml-name-validator')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'require valid attribute names',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-attribute-name.html'
    },
    fixable: null,
    schema: [],
    messages: {
      attribute: 'Attribute name {{name}} is not valid.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @param {string | VIdentifier} key
     * @return {string}
     */
    const getName = (key) => (typeof key === 'string' ? key : key.name)

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective | VAttribute} node */
      VAttribute(node) {
        if (utils.isCustomComponent(node.parent.parent)) {
          return
        }

        const name = getName(node.key.name)

        if (
          node.directive &&
          name === 'bind' &&
          node.key.argument &&
          node.key.argument.type === 'VIdentifier' &&
          !xnv.name(node.key.argument.name)
        ) {
          context.report({
            node,
            messageId: 'attribute',
            data: {
              name: node.key.argument.name
            }
          })
        }

        if (!node.directive && !xnv.name(name)) {
          context.report({
            node,
            messageId: 'attribute',
            data: {
              name
            }
          })
        }
      }
    })
  }
}

license: mit


code:
/**
 * @author Yosuke Ota <https://github.com/ota-meshi>
 * See LICENSE file in root directory for full license.
 */
'use strict'

const { findVariable } = require('@eslint-community/eslint-utils')
const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `defineEmits` compiler macro',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-define-emits.html'
    },
    fixable: null,
    schema: [],
    messages: {
      hasTypeAndArg: '`defineEmits` has both a type-only emit and an argument.',
      referencingLocally:
        '`defineEmits` is referencing locally declared variables.',
      multiple: '`defineEmits` has been called multiple times.',
      notDefined: 'Custom events are not defined.',
      definedInBoth:
        'Custom events are defined in both `defineEmits` and `export default {}`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const scriptSetup = utils.getScriptSetupElement(context)
    if (!scriptSetup) {
      return {}
    }

    /** @type {Set<Expression | SpreadElement>} */
    const emitsDefExpressions = new Set()
    let hasDefaultExport = false
    /** @type {CallExpression[]} */
    const defineEmitsNodes = []
    /** @type {CallExpression | null} */
    let emptyDefineEmits = null

    return utils.compositingVisitors(
      utils.defineScriptSetupVisitor(context, {
        onDefineEmitsEnter(node) {
          defineEmitsNodes.push(node)

          const typeArguments =
            'typeArguments' in node ? node.typeArguments : node.typeParameters
          if (node.arguments.length > 0) {
            if (typeArguments && typeArguments.params.length > 0) {
              // `defineEmits` has both a literal type and an argument.
              context.report({
                node,
                messageId: 'hasTypeAndArg'
              })
              return
            }

            emitsDefExpressions.add(node.arguments[0])
          } else {
            if (!typeArguments || typeArguments.params.length === 0) {
              emptyDefineEmits = node
            }
          }
        },
        Identifier(node) {
          for (const defineEmits of emitsDefExpressions) {
            if (utils.inRange(defineEmits.range, node)) {
              const variable = findVariable(utils.getScope(context, node), node)
              if (
                variable &&
                variable.references.some((ref) => ref.identifier === node) &&
                variable.defs.length > 0 &&
                variable.defs.every(
                  (def) =>
                    def.type !== 'ImportBinding' &&
                    utils.inRange(scriptSetup.range, def.name) &&
                    !utils.inRange(defineEmits.range, def.name)
                )
              ) {
                if (utils.withinTypeNode(node)) {
                  continue
                }
                //`defineEmits` is referencing locally declared variables.
                context.report({
                  node,
                  messageId: 'referencingLocally'
                })
              }
            }
          }
        }
      }),
      utils.defineVueVisitor(context, {
        onVueObjectEnter(node, { type }) {
          if (type !== 'export' || utils.inRange(scriptSetup.range, node)) {
            return
          }

          hasDefaultExport = Boolean(utils.findProperty(node, 'emits'))
        }
      }),
      {
        'Program:exit'() {
          if (defineEmitsNodes.length === 0) {
            return
          }
          if (defineEmitsNodes.length > 1) {
            // `defineEmits` has been called multiple times.
            for (const node of defineEmitsNodes) {
              context.report({
                node,
                messageId: 'multiple'
              })
            }
            return
          }
          if (emptyDefineEmits) {
            if (!hasDefaultExport) {
              // Custom events are not defined.
              context.report({
                node: emptyDefineEmits,
                messageId: 'notDefined'
              })
            }
          } else {
            if (hasDefaultExport) {
              // Custom events are defined in both `defineEmits` and `export default {}`.
              for (const node of defineEmitsNodes) {
                context.report({
                  node,
                  messageId: 'definedInBoth'
                })
              }
            }
          }
        }
      }
    )
  }
}


code:
/**
 * @author Yosuke Ota <https://github.com/ota-meshi>
 * See LICENSE file in root directory for full license.
 */
'use strict'

const { findVariable } = require('@eslint-community/eslint-utils')
const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `defineOptions` compiler macro',
      // TODO Switch in the next major version
      // categories: ['vue3-essential', 'vue2-essential'],
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/valid-define-options.html'
    },
    fixable: null,
    schema: [],
    messages: {
      referencingLocally:
        '`defineOptions` is referencing locally declared variables.',
      multiple: '`defineOptions` has been called multiple times.',
      notDefined: 'Options are not defined.',
      disallowProp:
        '`defineOptions()` cannot be used to declare `{{propName}}`. Use `{{insteadMacro}}()` instead.',
      typeArgs: '`defineOptions()` cannot accept type arguments.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const scriptSetup = utils.getScriptSetupElement(context)
    if (!scriptSetup) {
      return {}
    }

    /** @type {Set<Expression | SpreadElement>} */
    const optionsDefExpressions = new Set()
    /** @type {CallExpression[]} */
    const defineOptionsNodes = []

    return utils.compositingVisitors(
      utils.defineScriptSetupVisitor(context, {
        onDefineOptionsEnter(node) {
          defineOptionsNodes.push(node)

          if (node.arguments.length > 0) {
            const define = node.arguments[0]
            if (define.type === 'ObjectExpression') {
              for (const [propName, insteadMacro] of [
                ['props', 'defineProps'],
                ['emits', 'defineEmits'],
                ['expose', 'defineExpose'],
                ['slots', 'defineSlots']
              ]) {
                const prop = utils.findProperty(define, propName)
                if (prop) {
                  context.report({
                    node,
                    messageId: 'disallowProp',
                    data: { propName, insteadMacro }
                  })
                }
              }
            }

            optionsDefExpressions.add(node.arguments[0])
          } else {
            context.report({
              node,
              messageId: 'notDefined'
            })
          }

          const typeArguments =
            'typeArguments' in node ? node.typeArguments : node.typeParameters
          if (typeArguments) {
            context.report({
              node: typeArguments,
              messageId: 'typeArgs'
            })
          }
        },
        Identifier(node) {
          for (const defineOptions of optionsDefExpressions) {
            if (utils.inRange(defineOptions.range, node)) {
              const variable = findVariable(utils.getScope(context, node), node)
              if (
                variable &&
                variable.references.some((ref) => ref.identifier === node) &&
                variable.defs.length > 0 &&
                variable.defs.every(
                  (def) =>
                    def.type !== 'ImportBinding' &&
                    utils.inRange(scriptSetup.range, def.name) &&
                    !utils.inRange(defineOptions.range, def.name)
                )
              ) {
                if (utils.withinTypeNode(node)) {
                  continue
                }
                //`defineOptions` is referencing locally declared variables.
                context.report({
                  node,
                  messageId: 'referencingLocally'
                })
              }
            }
          }
        }
      }),
      {
        'Program:exit'() {
          if (defineOptionsNodes.length > 1) {
            // `defineOptions` has been called multiple times.
            for (const node of defineOptionsNodes) {
              context.report({
                node,
                messageId: 'multiple'
              })
            }
          }
        }
      }
    )
  }
}


code:
/**
 * @author Yosuke Ota <https://github.com/ota-meshi>
 * See LICENSE file in root directory for full license.
 */
'use strict'

const { findVariable } = require('@eslint-community/eslint-utils')
const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `defineProps` compiler macro',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-define-props.html'
    },
    fixable: null,
    schema: [],
    messages: {
      hasTypeAndArg:
        '`defineProps` has both a type-only props and an argument.',
      referencingLocally:
        '`defineProps` is referencing locally declared variables.',
      multiple: '`defineProps` has been called multiple times.',
      notDefined: 'Props are not defined.',
      definedInBoth:
        'Props are defined in both `defineProps` and `export default {}`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const scriptSetup = utils.getScriptSetupElement(context)
    if (!scriptSetup) {
      return {}
    }

    /** @type {Set<Expression | SpreadElement>} */
    const propsDefExpressions = new Set()
    let hasDefaultExport = false
    /** @type {CallExpression[]} */
    const definePropsNodes = []
    /** @type {CallExpression | null} */
    let emptyDefineProps = null

    return utils.compositingVisitors(
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(node) {
          definePropsNodes.push(node)

          const typeArguments =
            'typeArguments' in node ? node.typeArguments : node.typeParameters
          if (node.arguments.length > 0) {
            if (typeArguments && typeArguments.params.length > 0) {
              // `defineProps` has both a literal type and an argument.
              context.report({
                node,
                messageId: 'hasTypeAndArg'
              })
              return
            }

            propsDefExpressions.add(node.arguments[0])
          } else {
            if (!typeArguments || typeArguments.params.length === 0) {
              emptyDefineProps = node
            }
          }
        },
        Identifier(node) {
          for (const defineProps of propsDefExpressions) {
            if (utils.inRange(defineProps.range, node)) {
              const variable = findVariable(utils.getScope(context, node), node)
              if (
                variable &&
                variable.references.some((ref) => ref.identifier === node) &&
                variable.defs.length > 0 &&
                variable.defs.every(
                  (def) =>
                    def.type !== 'ImportBinding' &&
                    utils.inRange(scriptSetup.range, def.name) &&
                    !utils.inRange(defineProps.range, def.name)
                )
              ) {
                if (utils.withinTypeNode(node)) {
                  continue
                }
                //`defineProps` is referencing locally declared variables.
                context.report({
                  node,
                  messageId: 'referencingLocally'
                })
              }
            }
          }
        }
      }),
      utils.defineVueVisitor(context, {
        onVueObjectEnter(node, { type }) {
          if (type !== 'export' || utils.inRange(scriptSetup.range, node)) {
            return
          }

          hasDefaultExport = Boolean(utils.findProperty(node, 'props'))
        }
      }),
      {
        'Program:exit'() {
          if (definePropsNodes.length === 0) {
            return
          }
          if (definePropsNodes.length > 1) {
            // `defineProps` has been called multiple times.
            for (const node of definePropsNodes) {
              context.report({
                node,
                messageId: 'multiple'
              })
            }
            return
          }
          if (emptyDefineProps) {
            if (!hasDefaultExport) {
              // Props are not defined.
              context.report({
                node: emptyDefineProps,
                messageId: 'notDefined'
              })
            }
          } else {
            if (hasDefaultExport) {
              // Props are defined in both `defineProps` and `export default {}`.
              for (const node of definePropsNodes) {
                context.report({
                  node,
                  messageId: 'definedInBoth'
                })
              }
            }
          }
        }
      }
    )
  }
}


code:
/**
 * @fileoverview Requires valid keys in model option.
 * @author Alex Sokolov
 */
'use strict'

const utils = require('../utils')

const VALID_MODEL_KEYS = new Set(['prop', 'event'])

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'require valid keys in model option',
      categories: ['vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-model-definition.html'
    },
    fixable: null,
    schema: [],
    messages: {
      invalidKey: "Invalid key '{{name}}' in model option."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.executeOnVue(context, (obj) => {
      const modelProperty = utils.findProperty(obj, 'model')
      if (!modelProperty || modelProperty.value.type !== 'ObjectExpression') {
        return
      }

      for (const p of modelProperty.value.properties) {
        if (p.type !== 'Property') {
          continue
        }
        const name = utils.getStaticPropertyName(p)
        if (!name) {
          continue
        }
        if (!VALID_MODEL_KEYS.has(name)) {
          context.report({
            node: p,
            messageId: 'invalidKey',
            data: {
              name
            }
          })
        }
      }
    })
  }
}


code:
/**
 * @fileoverview enforce valid `nextTick` function calls
 * @author Flo Edelmann
 * @copyright 2021 Flo Edelmann. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')
const { findVariable } = require('@eslint-community/eslint-utils')

/**
 * @param {Identifier} identifier
 * @param {RuleContext} context
 * @returns {ASTNode|undefined}
 */
function getVueNextTickNode(identifier, context) {
  // Instance API: this.$nextTick()
  if (
    identifier.name === '$nextTick' &&
    identifier.parent.type === 'MemberExpression' &&
    utils.isThis(identifier.parent.object, context)
  ) {
    return identifier.parent
  }

  // Vue 2 Global API: Vue.nextTick()
  if (
    identifier.name === 'nextTick' &&
    identifier.parent.type === 'MemberExpression' &&
    identifier.parent.object.type === 'Identifier' &&
    identifier.parent.object.name === 'Vue'
  ) {
    return identifier.parent
  }

  // Vue 3 Global API: import { nextTick as nt } from 'vue'; nt()
  const variable = findVariable(utils.getScope(context, identifier), identifier)

  if (variable != null && variable.defs.length === 1) {
    const def = variable.defs[0]
    if (
      def.type === 'ImportBinding' &&
      def.node.type === 'ImportSpecifier' &&
      def.node.imported.type === 'Identifier' &&
      def.node.imported.name === 'nextTick' &&
      def.node.parent.type === 'ImportDeclaration' &&
      def.node.parent.source.value === 'vue'
    ) {
      return identifier
    }
  }

  return undefined
}

/**
 * @param {CallExpression} callExpression
 * @returns {boolean}
 */
function isAwaitedPromise(callExpression) {
  if (callExpression.parent.type === 'AwaitExpression') {
    // cases like `await nextTick()`
    return true
  }

  if (callExpression.parent.type === 'ReturnStatement') {
    // cases like `return nextTick()`
    return true
  }
  if (
    callExpression.parent.type === 'ArrowFunctionExpression' &&
    callExpression.parent.body === callExpression
  ) {
    // cases like `() => nextTick()`
    return true
  }

  if (
    callExpression.parent.type === 'MemberExpression' &&
    callExpression.parent.property.type === 'Identifier' &&
    callExpression.parent.property.name === 'then'
  ) {
    // cases like `nextTick().then()`
    return true
  }

  if (
    callExpression.parent.type === 'VariableDeclarator' ||
    callExpression.parent.type === 'AssignmentExpression'
  ) {
    // cases like `let foo = nextTick()` or `foo = nextTick()`
    return true
  }

  if (
    callExpression.parent.type === 'ArrayExpression' &&
    callExpression.parent.parent.type === 'CallExpression' &&
    callExpression.parent.parent.callee.type === 'MemberExpression' &&
    callExpression.parent.parent.callee.object.type === 'Identifier' &&
    callExpression.parent.parent.callee.object.name === 'Promise' &&
    callExpression.parent.parent.callee.property.type === 'Identifier'
  ) {
    // cases like `Promise.all([nextTick()])`
    return true
  }

  return false
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `nextTick` function calls',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-next-tick.html'
    },
    fixable: 'code',
    hasSuggestions: true,
    schema: [],
    messages: {
      shouldBeFunction: '`nextTick` is a function.',
      missingCallbackOrAwait:
        'Await the Promise returned by `nextTick` or pass a callback function.',
      addAwait: 'Add missing `await` statement.',
      tooManyParameters: '`nextTick` expects zero or one parameters.',
      eitherAwaitOrCallback:
        'Either await the Promise or pass a callback function to `nextTick`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineVueVisitor(context, {
      /** @param {Identifier} node */
      Identifier(node) {
        const nextTickNode = getVueNextTickNode(node, context)
        if (!nextTickNode || !nextTickNode.parent) {
          return
        }

        let parentNode = nextTickNode.parent

        // skip conditional expressions like `foo ? nextTick : bar`
        if (parentNode.type === 'ConditionalExpression') {
          parentNode = parentNode.parent
        }

        if (
          parentNode.type === 'CallExpression' &&
          parentNode.callee !== nextTickNode
        ) {
          // cases like `foo.then(nextTick)` are allowed
          return
        }

        if (
          parentNode.type === 'VariableDeclarator' ||
          parentNode.type === 'AssignmentExpression'
        ) {
          // cases like `let foo = nextTick` or `foo = nextTick` are allowed
          return
        }

        if (parentNode.type !== 'CallExpression') {
          context.report({
            node,
            messageId: 'shouldBeFunction',
            fix(fixer) {
              return fixer.insertTextAfter(node, '()')
            }
          })
          return
        }

        if (parentNode.arguments.length === 0) {
          if (!isAwaitedPromise(parentNode)) {
            context.report({
              node,
              messageId: 'missingCallbackOrAwait',
              suggest: [
                {
                  messageId: 'addAwait',
                  fix(fixer) {
                    return fixer.insertTextBefore(parentNode, 'await ')
                  }
                }
              ]
            })
          }
          return
        }

        if (parentNode.arguments.length > 1) {
          context.report({
            node,
            messageId: 'tooManyParameters'
          })
          return
        }

        if (isAwaitedPromise(parentNode)) {
          context.report({
            node,
            messageId: 'eitherAwaitOrCallback'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid template root',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-template-root.html'
    },
    fixable: null,
    schema: [],
    messages: {
      emptySrc:
        "The template root with 'src' attribute is required to be empty.",
      noChild: 'The template requires child element.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const sourceCode = context.getSourceCode()

    return {
      /** @param {Program} program */
      Program(program) {
        const element = program.templateBody
        if (element == null) {
          return
        }

        const hasSrc = utils.hasAttribute(element, 'src')
        const rootElements = []

        for (const child of element.children) {
          if (sourceCode.getText(child).trim() !== '') {
            rootElements.push(child)
          }
        }

        if (hasSrc && rootElements.length > 0) {
          for (const element of rootElements) {
            context.report({
              node: element,
              loc: element.loc,
              messageId: 'emptySrc'
            })
          }
        } else if (rootElements.length === 0 && !hasSrc) {
          context.report({
            node: element,
            loc: element.loc,
            messageId: 'noChild'
          })
        }
      }
    }
  }
}


code:
/**
 * @fileoverview enforce valid `.sync` modifier on `v-bind` directives
 * @author Yosuke Ota
 */
'use strict'

const utils = require('../utils')

/**
 * Check whether the given node is valid or not.
 * @param {VElement} node The element node to check.
 * @returns {boolean} `true` if the node is valid.
 */
function isValidElement(node) {
  if (!utils.isCustomComponent(node)) {
    // non Vue-component
    return false
  }
  return true
}

/**
 * Check whether the given node is a MemberExpression containing an optional chaining.
 * e.g.
 * - `a?.b`
 * - `a?.b.c`
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is a MemberExpression containing an optional chaining.
 */
function isOptionalChainingMemberExpression(node) {
  return (
    node.type === 'ChainExpression' &&
    node.expression.type === 'MemberExpression'
  )
}

/**
 * Check whether the given node can be LHS (left-hand side).
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node can be LHS.
 */
function isLhs(node) {
  return node.type === 'Identifier' || node.type === 'MemberExpression'
}

/**
 * Check whether the given node is a MemberExpression of a possibly null object.
 * e.g.
 * - `(a?.b).c`
 * - `(null).foo`
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is a MemberExpression of a possibly null object.
 */
function maybeNullObjectMemberExpression(node) {
  if (node.type !== 'MemberExpression') {
    return false
  }
  const { object } = node
  if (object.type === 'ChainExpression') {
    // `(a?.b).c`
    return true
  }
  if (object.type === 'Literal' && object.value === null && !object.bigint) {
    // `(null).foo`
    return true
  }
  if (object.type === 'MemberExpression') {
    return maybeNullObjectMemberExpression(object)
  }
  return false
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `.sync` modifier on `v-bind` directives',
      categories: ['vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-bind-sync.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedInvalidElement:
        "'.sync' modifiers aren't supported on <{{name}}> non Vue-components.",
      unexpectedOptionalChaining:
        "Optional chaining cannot appear in 'v-bind' with '.sync' modifiers.",
      unexpectedNonLhsExpression:
        "'.sync' modifiers require the attribute value which is valid as LHS.",
      unexpectedNullObject:
        "'.sync' modifier has potential null object property access.",
      unexpectedUpdateIterationVariable:
        "'.sync' modifiers cannot update the iteration variable '{{varName}}' itself."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='bind']"(node) {
        if (!node.key.modifiers.map((mod) => mod.name).includes('sync')) {
          return
        }
        const element = node.parent.parent
        const name = element.name

        if (!isValidElement(element)) {
          context.report({
            node,
            messageId: 'unexpectedInvalidElement',
            data: { name }
          })
        }

        if (!node.value) {
          return
        }

        const expression = node.value.expression
        if (!expression) {
          // Parsing error
          return
        }
        if (isOptionalChainingMemberExpression(expression)) {
          context.report({
            node: expression,
            messageId: 'unexpectedOptionalChaining'
          })
        } else if (!isLhs(expression)) {
          context.report({
            node: expression,
            messageId: 'unexpectedNonLhsExpression'
          })
        } else if (maybeNullObjectMemberExpression(expression)) {
          context.report({
            node: expression,
            messageId: 'unexpectedNullObject'
          })
        }

        for (const reference of node.value.references) {
          const id = reference.id
          if (id.parent.type !== 'VExpressionContainer') {
            continue
          }
          const variable = reference.variable
          if (variable) {
            context.report({
              node: expression,
              messageId: 'unexpectedUpdateIterationVariable',
              data: { varName: id.name }
            })
          }
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

const VALID_MODIFIERS = new Set(['prop', 'camel', 'sync', 'attr'])

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-bind` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-bind.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unsupportedModifier:
        "'v-bind' directives don't support the modifier '{{name}}'.",
      expectedValue: "'v-bind' directives require an attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='bind']"(node) {
        for (const modifier of node.key.modifiers) {
          if (!VALID_MODIFIERS.has(modifier.name)) {
            context.report({
              node: modifier,
              messageId: 'unsupportedModifier',
              data: { name: modifier.name }
            })
          }
        }

        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-cloak` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-cloak.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-cloak' directives require no argument.",
      unexpectedModifier: "'v-cloak' directives require no modifier.",
      unexpectedValue: "'v-cloak' directives require no attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='cloak']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (node.value) {
          context.report({
            node: node.value,
            messageId: 'unexpectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-else-if` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-else-if.html'
    },
    fixable: null,
    schema: [],
    messages: {
      missingVIf:
        "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive.",
      withVIf:
        "'v-else-if' and 'v-if' directives can't exist on the same element.",
      withVElse:
        "'v-else-if' and 'v-else' directives can't exist on the same element.",
      unexpectedArgument: "'v-else-if' directives require no argument.",
      unexpectedModifier: "'v-else-if' directives require no modifier.",
      expectedValue: "'v-else-if' directives require that attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='else-if']"(node) {
        const element = node.parent.parent

        if (!utils.prevElementHasIf(element)) {
          context.report({
            node,
            messageId: 'missingVIf'
          })
        }
        if (utils.hasDirective(element, 'if')) {
          context.report({
            node,
            messageId: 'withVIf'
          })
        }
        if (utils.hasDirective(element, 'else')) {
          context.report({
            node,
            messageId: 'withVElse'
          })
        }
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-else` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-else.html'
    },
    fixable: null,
    schema: [],
    messages: {
      missingVIf:
        "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive.",
      withVIf:
        "'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives.",
      withVElseIf:
        "'v-else' and 'v-else-if' directives can't exist on the same element.",
      unexpectedArgument: "'v-else' directives require no argument.",
      unexpectedModifier: "'v-else' directives require no modifier.",
      unexpectedValue: "'v-else' directives require no attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='else']"(node) {
        const element = node.parent.parent

        if (!utils.prevElementHasIf(element)) {
          context.report({
            node,
            messageId: 'missingVIf'
          })
        }
        if (utils.hasDirective(element, 'if')) {
          context.report({
            node,
            messageId: 'withVIf'
          })
        }
        if (utils.hasDirective(element, 'else-if')) {
          context.report({
            node,
            messageId: 'withVElseIf'
          })
        }
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (node.value) {
          context.report({
            node: node.value,
            messageId: 'unexpectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

/**
 * 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'
    )
  )
}

/**
 * Check the child element in tempalte v-for about `v-bind:key` attributes.
 * @param {RuleContext} context The rule context to report.
 * @param {VDirective} vFor The attribute node of `v-for` to check.
 * @param {VElement} child The child node to check.
 */
function checkChildKey(context, vFor, child) {
  const childFor = utils.getDirective(child, 'for')
  // if child has v-for, check if parent iterator is used in v-for
  if (childFor != null) {
    const childForRefs = (childFor.value && childFor.value.references) || []
    const variables = vFor.parent.parent.variables
    const usedInFor = childForRefs.some((cref) =>
      variables.some(
        (variable) =>
          cref.id.name === variable.id.name && variable.kind === 'v-for'
      )
    )
    // if parent iterator is used, skip other checks
    // iterator usage will be checked later by child v-for
    if (usedInFor) {
      return
    }
  }
  // otherwise, check if parent iterator is directly used in child's key
  checkKey(context, vFor, child)
}

/**
 * Check the given element about `v-bind:key` attributes.
 * @param {RuleContext} context The rule context to report.
 * @param {VDirective} vFor The attribute node of `v-for` to check.
 * @param {VElement} element The element node to check.
 */
function checkKey(context, vFor, element) {
  const vBindKey = utils.getDirective(element, 'bind', 'key')

  if (vBindKey == null && element.name === 'template') {
    for (const child of element.children) {
      if (child.type === 'VElement') {
        checkChildKey(context, vFor, child)
      }
    }
    return
  }

  if (utils.isCustomComponent(element) && vBindKey == null) {
    context.report({
      node: element.startTag,
      messageId: 'requireKey'
    })
  }
  if (vBindKey != null && !isUsingIterationVar(vFor, vBindKey)) {
    context.report({
      node: vBindKey,
      messageId: 'keyUseFVorVars'
    })
  }
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-for` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-for.html'
    },
    fixable: null,
    schema: [],
    messages: {
      requireKey:
        "Custom elements in iteration require 'v-bind:key' directives.",
      keyUseFVorVars:
        "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive.",
      unexpectedArgument: "'v-for' directives require no argument.",
      unexpectedModifier: "'v-for' directives require no modifier.",
      expectedValue: "'v-for' directives require that attribute value.",
      unexpectedExpression:
        "'v-for' directives require the special syntax '<alias> in <expression>'.",
      invalidEmptyAlias: "Invalid alias ''.",
      invalidAlias: "Invalid alias '{{text}}'."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const sourceCode = context.getSourceCode()

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='for']"(node) {
        const element = node.parent.parent

        checkKey(context, node, element)

        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
          return
        }

        const expr = node.value.expression
        if (expr == null) {
          return
        }
        if (expr.type !== 'VForExpression') {
          context.report({
            node: node.value,
            messageId: 'unexpectedExpression'
          })
          return
        }

        const lhs = expr.left
        const value = lhs[0]
        const key = lhs[1]
        const index = lhs[2]

        if (value === null) {
          context.report({
            node: expr,
            messageId: 'invalidEmptyAlias'
          })
        }
        if (key !== undefined && (!key || key.type !== 'Identifier')) {
          context.report({
            node: key || expr,
            messageId: 'invalidAlias',
            data: { text: key ? sourceCode.getText(key) : '' }
          })
        }
        if (index !== undefined && (!index || index.type !== 'Identifier')) {
          context.report({
            node: index || expr,
            messageId: 'invalidAlias',
            data: { text: index ? sourceCode.getText(index) : '' }
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-html` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-html.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-html' directives require no argument.",
      unexpectedModifier: "'v-html' directives require no modifier.",
      expectedValue: "'v-html' directives require that attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='html']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-if` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-if.html'
    },
    fixable: null,
    schema: [],
    messages: {
      withVElse:
        "'v-if' and 'v-else' directives can't exist on the same element. You may want 'v-else-if' directives.",
      withVElseIf:
        "'v-if' and 'v-else-if' directives can't exist on the same element.",
      unexpectedArgument: "'v-if' directives require no argument.",
      unexpectedModifier: "'v-if' directives require no modifier.",
      expectedValue: "'v-if' directives require that attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='if']"(node) {
        const element = node.parent.parent

        if (utils.hasDirective(element, 'else')) {
          context.report({
            node,
            messageId: 'withVElse'
          })
        }
        if (utils.hasDirective(element, 'else-if')) {
          context.report({
            node,
            messageId: 'withVElseIf'
          })
        }
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @fileoverview enforce valid `v-is` directives
 * @author Yosuke Ota
 */
'use strict'

const utils = require('../utils')

/**
 * Check whether the given node is valid or not.
 * @param {VElement} node The element node to check.
 * @returns {boolean} `true` if the node is valid.
 */
function isValidElement(node) {
  if (
    utils.isHtmlElementNode(node) &&
    !utils.isHtmlWellKnownElementName(node.rawName)
  ) {
    // Vue-component
    return false
  }
  return true
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-is` directives',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-is.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-is' directives require no argument.",
      unexpectedModifier: "'v-is' directives require no modifier.",
      expectedValue: "'v-is' directives require that attribute value.",
      ownerMustBeHTMLElement:
        "'v-is' directive must be owned by a native HTML element, but '{{name}}' is not."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      "VAttribute[directive=true][key.name.name='is']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }

        const element = node.parent.parent

        if (!isValidElement(element)) {
          const name = element.name
          context.report({
            node,
            messageId: 'ownerMustBeHTMLElement',
            data: { name }
          })
        }
      }
    })
  }
}


code:
/**
 * @author Yosuke Ota <https://github.com/ota-meshi>
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-memo` directives',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-memo.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-memo' directives require no argument.",
      unexpectedModifier: "'v-memo' directives require no modifier.",
      expectedValue: "'v-memo' directives require that attribute value.",
      expectedArray:
        "'v-memo' directives require the attribute value to be an array.",
      insideVFor: "'v-memo' directive does not work inside 'v-for'."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {VElement | null} */
    let vForElement = null
    return utils.defineTemplateBodyVisitor(context, {
      VElement(node) {
        if (!vForElement && utils.hasDirective(node, 'for')) {
          vForElement = node
        }
      },
      'VElement:exit'(node) {
        if (vForElement === node) {
          vForElement = null
        }
      },
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='memo']"(node) {
        if (vForElement && vForElement !== node.parent.parent) {
          context.report({
            node: node.key,
            messageId: 'insideVFor'
          })
        }
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
          return
        }
        if (!node.value.expression) {
          return
        }
        const expressions = [node.value.expression]
        let expression
        while ((expression = expressions.pop())) {
          switch (expression.type) {
            case 'ObjectExpression':
            case 'ClassExpression':
            case 'ArrowFunctionExpression':
            case 'FunctionExpression':
            case 'Literal':
            case 'TemplateLiteral':
            case 'UnaryExpression':
            case 'BinaryExpression':
            case 'UpdateExpression': {
              context.report({
                node: expression,
                messageId: 'expectedArray'
              })
              break
            }
            case 'AssignmentExpression': {
              expressions.push(expression.right)
              break
            }
            case 'TSAsExpression': {
              expressions.push(expression.expression)
              break
            }
            case 'SequenceExpression': {
              expressions.push(
                expression.expressions[expression.expressions.length - 1]
              )
              break
            }
            case 'ConditionalExpression': {
              expressions.push(expression.consequent, expression.alternate)
              break
            }
          }
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])

/**
 * Check whether the given node is valid or not.
 * @param {VElement} 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 is a MemberExpression containing an optional chaining.
 * e.g.
 * - `a?.b`
 * - `a?.b.c`
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is a MemberExpression containing an optional chaining.
 */
function isOptionalChainingMemberExpression(node) {
  return (
    node.type === 'ChainExpression' &&
    node.expression.type === 'MemberExpression'
  )
}

/**
 * Check whether the given node can be LHS (left-hand side).
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node can be LHS.
 */
function isLhs(node) {
  if (node.type === 'TSAsExpression' || node.type === 'TSNonNullExpression') {
    return isLhs(node.expression)
  }

  return node.type === 'Identifier' || node.type === 'MemberExpression'
}

/**
 * Check whether the given node is a MemberExpression of a possibly null object.
 * e.g.
 * - `(a?.b).c`
 * - `(null).foo`
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is a MemberExpression of a possibly null object.
 */
function maybeNullObjectMemberExpression(node) {
  if (node.type !== 'MemberExpression') {
    return false
  }
  const { object } = node
  if (object.type === 'ChainExpression') {
    // `(a?.b).c`
    return true
  }
  if (object.type === 'Literal' && object.value === null && !object.bigint) {
    // `(null).foo`
    return true
  }
  if (object.type === 'MemberExpression') {
    return maybeNullObjectMemberExpression(object)
  }
  return false
}

/**
 * Get the variable by names.
 * @param {string} name The variable name to find.
 * @param {VElement} leafNode The node to look up.
 * @returns {VVariable|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
    }

    if (node.parent.type === 'VDocumentFragment') {
      break
    }

    node = node.parent
  }

  return null
}

/** @type {RuleModule}  */
module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-model` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-model.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedInvalidElement:
        "'v-model' directives aren't supported on <{{name}}> elements.",
      unexpectedInputFile:
        "'v-model' directives don't support 'file' input type.",
      unexpectedArgument: "'v-model' directives require no argument.",
      unexpectedModifier:
        "'v-model' directives don't support the modifier '{{name}}'.",
      missingValue: "'v-model' directives require that attribute value.",
      unexpectedOptionalChaining:
        "Optional chaining cannot appear in 'v-model' directives.",
      unexpectedNonLhsExpression:
        "'v-model' directives require the attribute value which is valid as LHS.",
      unexpectedNullObject:
        "'v-model' directive has potential null object property access.",
      unexpectedUpdateIterationVariable:
        "'v-model' directives cannot update the iteration variable '{{varName}}' itself."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='model']"(node) {
        const element = node.parent.parent
        const name = element.name

        if (!isValidElement(element)) {
          context.report({
            node,
            messageId: 'unexpectedInvalidElement',
            data: { name }
          })
        }

        if (name === 'input' && utils.hasAttribute(element, 'type', 'file')) {
          context.report({
            node,
            messageId: 'unexpectedInputFile'
          })
        }

        if (!utils.isCustomComponent(element)) {
          if (node.key.argument) {
            context.report({
              node: node.key.argument,
              messageId: 'unexpectedArgument'
            })
          }

          for (const modifier of node.key.modifiers) {
            if (!VALID_MODIFIERS.has(modifier.name)) {
              context.report({
                node: modifier,
                messageId: 'unexpectedModifier',
                data: { name: modifier.name }
              })
            }
          }
        }

        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'missingValue'
          })
          return
        }
        const expression = node.value.expression
        if (!expression) {
          // Parsing error
          return
        }
        if (isOptionalChainingMemberExpression(expression)) {
          context.report({
            node: expression,
            messageId: 'unexpectedOptionalChaining'
          })
        } else if (!isLhs(expression)) {
          context.report({
            node: expression,
            messageId: 'unexpectedNonLhsExpression'
          })
        } else if (maybeNullObjectMemberExpression(expression)) {
          context.report({
            node: expression,
            messageId: 'unexpectedNullObject'
          })
        }

        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: expression,
              messageId: 'unexpectedUpdateIterationVariable',

              data: { varName: id.name }
            })
          }
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')
const keyAliases = require('../utils/key-aliases.json')

const VALID_MODIFIERS = new Set([
  'stop',
  'prevent',
  'capture',
  'self',
  'ctrl',
  'shift',
  'alt',
  'meta',
  'native',
  'once',
  'left',
  'right',
  'middle',
  'passive',
  'esc',
  'tab',
  'enter',
  'space',
  'up',
  'left',
  'right',
  'down',
  'delete',
  'exact'
])
const VERB_MODIFIERS = new Set(['stop', 'prevent'])
// https://www.w3.org/TR/uievents-key/
const KEY_ALIASES = new Set(keyAliases)

/**
 * @param {VIdentifier} modifierNode
 * @param {Set<string>} customModifiers
 */
function isValidModifier(modifierNode, customModifiers) {
  const modifier = modifierNode.name
  return (
    // built-in aliases
    VALID_MODIFIERS.has(modifier) ||
    // keyCode
    Number.isInteger(Number.parseInt(modifier, 10)) ||
    // keyAlias (an Unicode character)
    [...modifier].length === 1 ||
    // keyAlias (special keys)
    KEY_ALIASES.has(modifier) ||
    // custom modifiers
    customModifiers.has(modifier)
  )
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-on` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-on.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          modifiers: {
            type: 'array'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      unsupportedModifier:
        "'v-on' directives don't support the modifier '{{modifier}}'.",
      avoidKeyword:
        'Avoid using JavaScript keyword as "v-on" value: {{value}}.',
      expectedValueOrVerb:
        "'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    /** @type {Set<string>} */
    const customModifiers = new Set(options.modifiers || [])
    const sourceCode = context.getSourceCode()

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='on']"(node) {
        for (const modifier of node.key.modifiers) {
          if (!isValidModifier(modifier, customModifiers)) {
            context.report({
              node: modifier,
              messageId: 'unsupportedModifier',
              data: { modifier: modifier.name }
            })
          }
        }

        if (
          (!node.value || !node.value.expression) &&
          !node.key.modifiers.some((modifier) =>
            VERB_MODIFIERS.has(modifier.name)
          )
        ) {
          if (node.value && !utils.isEmptyValueDirective(node, context)) {
            const valueText = sourceCode.getText(node.value)
            let innerText = valueText
            if (
              (valueText[0] === '"' || valueText[0] === "'") &&
              valueText[0] === valueText[valueText.length - 1]
            ) {
              // quoted
              innerText = valueText.slice(1, -1)
            }
            if (/^\w+$/.test(innerText)) {
              context.report({
                node: node.value,
                messageId: 'avoidKeyword',
                data: { value: valueText }
              })
            }
          } else {
            context.report({
              node,
              messageId: 'expectedValueOrVerb'
            })
          }
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-once` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-once.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-once' directives require no argument.",
      unexpectedModifier: "'v-once' directives require no modifier.",
      unexpectedValue: "'v-once' directives require no attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='once']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (node.value) {
          context.report({
            node: node.value,
            messageId: 'unexpectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-pre` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-pre.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-pre' directives require no argument.",
      unexpectedModifier: "'v-pre' directives require no modifier.",
      unexpectedValue: "'v-pre' directives require no attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='pre']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (node.value) {
          context.report({
            node: node.value,
            messageId: 'unexpectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-show` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-show.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-show' directives require no argument.",
      unexpectedModifier: "'v-show' directives require no modifier.",
      expectedValue: "'v-show' directives require that attribute value.",
      unexpectedTemplate:
        "'v-show' directives cannot be put on <template> tags."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='show']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }
        if (node.parent.parent.name === 'template') {
          context.report({
            node,
            messageId: 'unexpectedTemplate'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

/**
 * @typedef { { expr: VForExpression, variables: VVariable[] } } VSlotVForVariables
 */

/**
 * Get all `v-slot` directives on a given element.
 * @param {VElement} node The VElement node to check.
 * @returns {VDirective[]} The array of `v-slot` directives.
 */
function getSlotDirectivesOnElement(node) {
  return utils.getDirectives(node, 'slot')
}

/**
 * Get all `v-slot` directives on the children of a given element.
 * @param {VElement} node The VElement node to check.
 * @returns {VDirective[][]}
 * The array of the group of `v-slot` directives.
 * The group bundles `v-slot` directives of element sequence which is connected
 * by `v-if`/`v-else-if`/`v-else`.
 */
function getSlotDirectivesOnChildren(node) {
  /** @type {VDirective[][]} */
  const groups = []
  for (const group of utils.iterateChildElementsChains(node)) {
    const slotDirs = group
      .map((childElement) =>
        childElement.name === 'template'
          ? utils.getDirective(childElement, 'slot')
          : null
      )
      .filter(utils.isDef)
    if (slotDirs.length > 0) {
      groups.push(slotDirs)
    }
  }

  return groups
}

/**
 * Get the normalized name of a given `v-slot` directive node with modifiers after `v-slot:` directive.
 * @param {VDirective} node The `v-slot` directive node.
 * @param {SourceCode} sourceCode The source code.
 * @returns {string} The normalized name.
 */
function getNormalizedName(node, sourceCode) {
  if (node.key.argument == null) {
    return 'default'
  }
  return node.key.modifiers.length === 0
    ? sourceCode.getText(node.key.argument)
    : sourceCode.text.slice(node.key.argument.range[0], node.key.range[1])
}

/**
 * Get all `v-slot` directives which are distributed to the same slot as a given `v-slot` directive node.
 * @param {VDirective[][]} vSlotGroups The result of `getAllNamedSlotElements()`.
 * @param {VDirective} currentVSlot The current `v-slot` directive node.
 * @param {VSlotVForVariables | null} currentVSlotVForVars The current `v-for` variables.
 * @param {SourceCode} sourceCode The source code.
 * @param {ParserServices.TokenStore} tokenStore The token store.
 * @returns {VDirective[][]} The array of the group of `v-slot` directives.
 */
function filterSameSlot(
  vSlotGroups,
  currentVSlot,
  currentVSlotVForVars,
  sourceCode,
  tokenStore
) {
  const currentName = getNormalizedName(currentVSlot, sourceCode)
  return vSlotGroups
    .map((vSlots) =>
      vSlots.filter((vSlot) => {
        if (getNormalizedName(vSlot, sourceCode) !== currentName) {
          return false
        }
        const vForExpr = getVSlotVForVariableIfUsingIterationVars(
          vSlot,
          utils.getDirective(vSlot.parent.parent, 'for')
        )
        if (!currentVSlotVForVars || !vForExpr) {
          return !currentVSlotVForVars && !vForExpr
        }
        if (
          !equalVSlotVForVariables(currentVSlotVForVars, vForExpr, tokenStore)
        ) {
          return false
        }
        //
        return true
      })
    )
    .filter((slots) => slots.length > 0)
}

/**
 * Determines whether the two given `v-slot` variables are considered to be equal.
 * @param {VSlotVForVariables} a First element.
 * @param {VSlotVForVariables} b Second element.
 * @param {ParserServices.TokenStore} tokenStore The token store.
 * @returns {boolean} `true` if the elements are considered to be equal.
 */
function equalVSlotVForVariables(a, b, tokenStore) {
  if (a.variables.length !== b.variables.length) {
    return false
  }
  if (!equal(a.expr.right, b.expr.right)) {
    return false
  }

  const checkedVarNames = new Set()
  const len = Math.min(a.expr.left.length, b.expr.left.length)
  for (let index = 0; index < len; index++) {
    const aPtn = a.expr.left[index]
    const bPtn = b.expr.left[index]

    const aVar = a.variables.find(
      (v) => aPtn.range[0] <= v.id.range[0] && v.id.range[1] <= aPtn.range[1]
    )
    const bVar = b.variables.find(
      (v) => bPtn.range[0] <= v.id.range[0] && v.id.range[1] <= bPtn.range[1]
    )
    if (aVar && bVar) {
      if (aVar.id.name !== bVar.id.name) {
        return false
      }
      if (!equal(aPtn, bPtn)) {
        return false
      }
      checkedVarNames.add(aVar.id.name)
    } else if (aVar || bVar) {
      return false
    }
  }
  return a.variables.every(
    (v) =>
      checkedVarNames.has(v.id.name) ||
      b.variables.some((bv) => v.id.name === bv.id.name)
  )

  /**
   * Determines whether the two given nodes are considered to be equal.
   * @param {ASTNode} a First node.
   * @param {ASTNode} b Second node.
   * @returns {boolean} `true` if the nodes are considered to be equal.
   */
  function equal(a, b) {
    if (a.type !== b.type) {
      return false
    }
    return utils.equalTokens(a, b, tokenStore)
  }
}

/**
 * Gets the `v-for` directive and variable that provide the variables used by the given` v-slot` directive.
 * @param {VDirective} vSlot The current `v-slot` directive node.
 * @param {VDirective | null} [vFor] The current `v-for` directive node.
 * @returns { VSlotVForVariables | null } The VSlotVForVariable.
 */
function getVSlotVForVariableIfUsingIterationVars(vSlot, vFor) {
  const expr =
    vFor && vFor.value && /** @type {VForExpression} */ (vFor.value.expression)
  const variables =
    expr && getUsingIterationVars(vSlot.key.argument, vSlot.parent.parent)
  return expr && variables && variables.length > 0 ? { expr, variables } : null
}

/**
 * Gets iterative variables if a given argument node is using iterative variables that the element defined.
 * @param {VExpressionContainer|VIdentifier|null} argument The argument node to check.
 * @param {VElement} element The element node which has the argument.
 * @returns {VVariable[]} The argument node is using iteration variables.
 */
function getUsingIterationVars(argument, element) {
  const vars = []
  if (argument && argument.type === 'VExpressionContainer') {
    for (const { variable } of argument.references) {
      if (
        variable != null &&
        variable.kind === 'v-for' &&
        variable.id.range[0] > element.startTag.range[0] &&
        variable.id.range[1] < element.startTag.range[1]
      ) {
        vars.push(variable)
      }
    }
  }
  return vars
}

/**
 * Check whether a given argument node is using an scope variable that the directive defined.
 * @param {VDirective} vSlot The `v-slot` directive to check.
 * @returns {boolean} `true` if that argument node is using a scope variable the directive defined.
 */
function isUsingScopeVar(vSlot) {
  const argument = vSlot.key.argument
  const value = vSlot.value

  if (argument && value && argument.type === 'VExpressionContainer') {
    for (const { variable } of argument.references) {
      if (
        variable != null &&
        variable.kind === 'scope' &&
        variable.id.range[0] > value.range[0] &&
        variable.id.range[1] < value.range[1]
      ) {
        return true
      }
    }
  }
  return false
}

/**
 * If `allowModifiers` option is set to `true`, check whether a given argument node has invalid modifiers like `v-slot.foo`.
 * Otherwise, check whether a given argument node has at least one modifier.
 * @param {VDirective} vSlot The `v-slot` directive to check.
 * @param {boolean} allowModifiers `allowModifiers` option in context.
 * @return {boolean} `true` if that argument node has invalid modifiers like `v-slot.foo`.
 */
function hasInvalidModifiers(vSlot, allowModifiers) {
  return allowModifiers
    ? vSlot.key.argument == null && vSlot.key.modifiers.length > 0
    : vSlot.key.modifiers.length > 0
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-slot` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-slot.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          allowModifiers: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      ownerMustBeCustomElement:
        "'v-slot' directive must be owned by a custom element, but '{{name}}' is not.",
      namedSlotMustBeOnTemplate:
        "Named slots must use '<template>' on a custom element.",
      defaultSlotMustBeOnTemplate:
        "Default slot must use '<template>' on a custom element when there are other named slots.",
      disallowDuplicateSlotsOnElement:
        "An element cannot have multiple 'v-slot' directives.",
      disallowDuplicateSlotsOnChildren:
        "An element cannot have multiple '<template>' elements which are distributed to the same slot.",
      disallowArgumentUseSlotParams:
        "Dynamic argument of 'v-slot' directive cannot use that slot parameter.",
      disallowAnyModifier: "'v-slot' directive doesn't support any modifier.",
      requireAttributeValue:
        "'v-slot' directive on a custom element requires that attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const sourceCode = context.getSourceCode()
    const tokenStore =
      sourceCode.parserServices.getTemplateBodyTokenStore &&
      sourceCode.parserServices.getTemplateBodyTokenStore()
    const options = context.options[0] || {}
    const allowModifiers = options.allowModifiers === true

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='slot']"(node) {
        const isDefaultSlot =
          node.key.argument == null ||
          (node.key.argument.type === 'VIdentifier' &&
            node.key.argument.name === 'default')
        const element = node.parent.parent
        const parentElement = element.parent
        const ownerElement =
          element.name === 'template' ? parentElement : element
        if (ownerElement.type === 'VDocumentFragment') {
          return
        }
        const vSlotsOnElement = getSlotDirectivesOnElement(element)
        const vSlotGroupsOnChildren = getSlotDirectivesOnChildren(ownerElement)

        // Verify location.
        if (!utils.isCustomComponent(ownerElement)) {
          context.report({
            node,
            messageId: 'ownerMustBeCustomElement',
            data: { name: ownerElement.rawName }
          })
        }
        if (!isDefaultSlot && element.name !== 'template') {
          context.report({
            node,
            messageId: 'namedSlotMustBeOnTemplate'
          })
        }
        if (ownerElement === element && vSlotGroupsOnChildren.length > 0) {
          context.report({
            node,
            messageId: 'defaultSlotMustBeOnTemplate'
          })
        }

        // Verify duplication.
        if (vSlotsOnElement.length >= 2 && vSlotsOnElement[0] !== node) {
          // E.g., <my-component #one #two>
          context.report({
            node,
            messageId: 'disallowDuplicateSlotsOnElement'
          })
        }
        if (ownerElement === parentElement) {
          const vFor = utils.getDirective(element, 'for')
          const vSlotVForVar = getVSlotVForVariableIfUsingIterationVars(
            node,
            vFor
          )
          const vSlotGroupsOfSameSlot = filterSameSlot(
            vSlotGroupsOnChildren,
            node,
            vSlotVForVar,
            sourceCode,
            tokenStore
          )
          if (
            vSlotGroupsOfSameSlot.length >= 2 &&
            !vSlotGroupsOfSameSlot[0].includes(node)
          ) {
            // E.g., <template #one></template>
            //       <template #one></template>
            context.report({
              node,
              messageId: 'disallowDuplicateSlotsOnChildren'
            })
          }
          if (vFor && !vSlotVForVar) {
            // E.g., <template v-for="x of xs" #one></template>
            context.report({
              node,
              messageId: 'disallowDuplicateSlotsOnChildren'
            })
          }
        }

        // Verify argument.
        if (isUsingScopeVar(node)) {
          context.report({
            node,
            messageId: 'disallowArgumentUseSlotParams'
          })
        }

        // Verify modifiers.
        if (hasInvalidModifiers(node, allowModifiers)) {
          // E.g., <template v-slot.foo>
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'disallowAnyModifier'
          })
        }

        // Verify value.
        if (
          ownerElement === element &&
          isDefaultSlot &&
          (!node.value ||
            utils.isEmptyValueDirective(node, context) ||
            utils.isEmptyExpressionValueDirective(node, context))
        ) {
          context.report({
            node,
            messageId: 'requireAttributeValue'
          })
        }
      }
    })
  }
}


code:
/**
 * @author Toru Nagashima
 * @copyright 2017 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
'use strict'

const utils = require('../utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce valid `v-text` directives',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/valid-v-text.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedArgument: "'v-text' directives require no argument.",
      unexpectedModifier: "'v-text' directives require no modifier.",
      expectedValue: "'v-text' directives require that attribute value."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='text']"(node) {
        if (node.key.argument) {
          context.report({
            node: node.key.argument,
            messageId: 'unexpectedArgument'
          })
        }
        if (node.key.modifiers.length > 0) {
          context.report({
            node,
            loc: {
              start: node.key.modifiers[0].loc.start,
              end: node.key.modifiers[node.key.modifiers.length - 1].loc.end
            },
            messageId: 'unexpectedModifier'
          })
        }
        if (!node.value || utils.isEmptyValueDirective(node, context)) {
          context.report({
            node,
            messageId: 'expectedValue'
          })
        }
      }
    })
  }
}


code:
/**
 * Capitalize a string.
 * @param {string} str
 */
function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
 * Checks whether the given string has symbols.
 * @param {string} str
 */
function hasSymbols(str) {
  return /[!"#%&'()*+,./:;<=>?@[\\\]^`{|}]/u.exec(str) // without " ", "$", "-" and "_"
}
/**
 * Checks whether the given string has upper.
 * @param {string} str
 */
function hasUpper(str) {
  return /[A-Z]/u.exec(str)
}

/**
 * Convert text to kebab-case
 * @param {string} str Text to be converted
 * @return {string}
 */
function kebabCase(str) {
  return str
    .replace(/_/gu, '-')
    .replace(/\B([A-Z])/gu, '-$1')
    .toLowerCase()
}

/**
 * Checks whether the given string is kebab-case.
 * @param {string} str
 */
function isKebabCase(str) {
  return (
    !hasUpper(str) &&
    !hasSymbols(str) &&
    !str.startsWith('-') && // starts with hyphen is not kebab-case
    !/_|--|\s/u.test(str)
  )
}

/**
 * Convert text to snake_case
 * @param {string} str Text to be converted
 * @return {string}
 */
function snakeCase(str) {
  return str
    .replace(/\B([A-Z])/gu, '_$1')
    .replace(/-/gu, '_')
    .toLowerCase()
}

/**
 * Checks whether the given string is snake_case.
 * @param {string} str
 */
function isSnakeCase(str) {
  return !hasUpper(str) && !hasSymbols(str) && !/-|__|\s/u.test(str)
}

/**
 * Convert text to camelCase
 * @param {string} str Text to be converted
 * @return {string} Converted string
 */
function camelCase(str) {
  if (isPascalCase(str)) {
    return str.charAt(0).toLowerCase() + str.slice(1)
  }
  return str.replace(/[-_](\w)/gu, (_, c) => (c ? c.toUpperCase() : ''))
}

/**
 * Checks whether the given string is camelCase.
 * @param {string} str
 */
function isCamelCase(str) {
  return !hasSymbols(str) && !/^[A-Z]/u.test(str) && !/-|_|\s/u.test(str)
}

/**
 * Convert text to PascalCase
 * @param {string} str Text to be converted
 * @return {string} Converted string
 */
function pascalCase(str) {
  return capitalize(camelCase(str))
}

/**
 * Checks whether the given string is PascalCase.
 * @param {string} str
 */
function isPascalCase(str) {
  return !hasSymbols(str) && !/^[a-z]/u.test(str) && !/-|_|\s/u.test(str)
}

const convertersMap = {
  'kebab-case': kebabCase,
  snake_case: snakeCase,
  camelCase,
  PascalCase: pascalCase
}

const checkersMap = {
  'kebab-case': isKebabCase,
  snake_case: isSnakeCase,
  camelCase: isCamelCase,
  PascalCase: isPascalCase
}
/**
 * Return case checker
 * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of checker to return ('camelCase', 'kebab-case', 'PascalCase')
 * @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase}
 */
function getChecker(name) {
  return checkersMap[name] || isPascalCase
}

/**
 * Return case converter
 * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase')
 * @return {kebabCase|camelCase|pascalCase|snakeCase}
 */
function getConverter(name) {
  return convertersMap[name] || pascalCase
}

module.exports = {
  allowedCaseOptions: ['camelCase', 'kebab-case', 'PascalCase'],

  /**
   * Return case converter
   * @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase')
   * @return {kebabCase|camelCase|pascalCase|snakeCase}
   */
  getConverter,

  /**
   * Return case checker
   * @param {string} name type of checker to return ('camelCase', 'kebab-case', 'PascalCase')
   * @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase}
   */
  getChecker,

  /**
   * Return case exact converter.
   * If the converted result is not the correct case, the original value is returned.
   * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase')
   * @return {kebabCase|camelCase|pascalCase|snakeCase}
   */
  getExactConverter(name) {
    const converter = getConverter(name)
    const checker = getChecker(name)
    return (str) => {
      const result = converter(str)
      return checker(result) ? result : str /* cannot convert */
    }
  },

  camelCase,
  pascalCase,
  kebabCase,
  snakeCase,

  isCamelCase,
  isPascalCase,
  isKebabCase,
  isSnakeCase,

  capitalize
}


code:
/**
 * @param {Comment} node
 * @returns {boolean}
 */
const isJSDocComment = (node) =>
  node.type === 'Block' &&
  node.value.charAt(0) === '*' &&
  node.value.charAt(1) !== '*'

/**
 * @param {Comment} node
 * @returns {boolean}
 */
const isBlockComment = (node) =>
  node.type === 'Block' &&
  (node.value.charAt(0) !== '*' || node.value.charAt(1) === '*')

module.exports = {
  isJSDocComment,
  isBlockComment
}


code: acronym applet basefont bgsound big blink center command content dir element font frame frameset image isindex keygen listing marquee menuitem multicol nextid nobr noembed noframes plaintext shadow spacer strike tt xmp


code:
/**
 * @typedef { { exceptions?: string[] } } CommentParserConfig
 * @typedef { (comment: ParsedHTMLComment) => void } HTMLCommentVisitor
 * @typedef { { includeDirectives?: boolean } } CommentVisitorOption
 *
 * @typedef { Token & { type: 'HTMLCommentOpen' } } HTMLCommentOpen
 * @typedef { Token & { type: 'HTMLCommentOpenDecoration' } } HTMLCommentOpenDecoration
 * @typedef { Token & { type: 'HTMLCommentValue' } } HTMLCommentValue
 * @typedef { Token & { type: 'HTMLCommentClose' } } HTMLCommentClose
 * @typedef { Token & { type: 'HTMLCommentCloseDecoration' } } HTMLCommentCloseDecoration
 * @typedef { { open: HTMLCommentOpen, openDecoration: HTMLCommentOpenDecoration | null, value: HTMLCommentValue | null, closeDecoration: HTMLCommentCloseDecoration | null, close: HTMLCommentClose } } ParsedHTMLComment
 */
const utils = require('./')

const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/
const IE_CONDITIONAL_IF = /^\[if\s+/
const IE_CONDITIONAL_ENDIF = /\[endif]$/

/** @type { 'HTMLCommentOpen' } */
const TYPE_HTML_COMMENT_OPEN = 'HTMLCommentOpen'
/** @type { 'HTMLCommentOpenDecoration' } */
const TYPE_HTML_COMMENT_OPEN_DECORATION = 'HTMLCommentOpenDecoration'
/** @type { 'HTMLCommentValue' } */
const TYPE_HTML_COMMENT_VALUE = 'HTMLCommentValue'
/** @type { 'HTMLCommentClose' } */
const TYPE_HTML_COMMENT_CLOSE = 'HTMLCommentClose'
/** @type { 'HTMLCommentCloseDecoration' } */
const TYPE_HTML_COMMENT_CLOSE_DECORATION = 'HTMLCommentCloseDecoration'

/**
 * @param {HTMLComment} comment
 * @returns {boolean}
 */
function isCommentDirective(comment) {
  return COMMENT_DIRECTIVE.test(comment.value)
}

/**
 * @param {HTMLComment} comment
 * @returns {boolean}
 */
function isIEConditionalComment(comment) {
  return (
    IE_CONDITIONAL_IF.test(comment.value) ||
    IE_CONDITIONAL_ENDIF.test(comment.value)
  )
}

/**
 * Define HTML comment parser
 *
 * @param {SourceCode} sourceCode The source code instance.
 * @param {CommentParserConfig | null} config The config.
 * @returns { (node: Token) => (ParsedHTMLComment | null) } HTML comment parser.
 */
function defineParser(sourceCode, config) {
  config = config || {}

  const exceptions = config.exceptions || []

  /**
   * Get a open decoration string from comment contents.
   * @param {string} contents comment contents
   * @returns {string} decoration string
   */
  function getOpenDecoration(contents) {
    let decoration = ''
    for (const exception of exceptions) {
      const length = exception.length
      let index = 0
      while (contents.startsWith(exception, index)) {
        index += length
      }
      const exceptionLength = index
      if (decoration.length < exceptionLength) {
        decoration = contents.slice(0, exceptionLength)
      }
    }
    return decoration
  }

  /**
   * Get a close decoration string from comment contents.
   * @param {string} contents comment contents
   * @returns {string} decoration string
   */
  function getCloseDecoration(contents) {
    let decoration = ''
    for (const exception of exceptions) {
      const length = exception.length
      let index = contents.length
      while (contents.endsWith(exception, index)) {
        index -= length
      }
      const exceptionLength = contents.length - index
      if (decoration.length < exceptionLength) {
        decoration = contents.slice(index)
      }
    }
    return decoration
  }

  /**
   * Parse HTMLComment.
   * @param {Token} node a comment token
   * @returns {ParsedHTMLComment | null} the result of HTMLComment tokens.
   */
  return function parseHTMLComment(node) {
    if (node.type !== 'HTMLComment') {
      // Is not HTMLComment
      return null
    }

    const htmlCommentText = sourceCode.getText(node)

    if (
      !htmlCommentText.startsWith('<!--') ||
      !htmlCommentText.endsWith('-->')
    ) {
      // Is not normal HTML Comment
      // e.g. Error Code: "abrupt-closing-of-empty-comment", "incorrectly-closed-comment"
      return null
    }

    let valueText = htmlCommentText.slice(4, -3)
    const openDecorationText = getOpenDecoration(valueText)
    valueText = valueText.slice(openDecorationText.length)
    const firstCharIndex = valueText.search(/\S/)
    const beforeSpace =
      firstCharIndex >= 0 ? valueText.slice(0, firstCharIndex) : valueText
    valueText = valueText.slice(beforeSpace.length)

    const closeDecorationText = getCloseDecoration(valueText)
    if (closeDecorationText) {
      valueText = valueText.slice(0, -closeDecorationText.length)
    }
    const lastCharIndex = valueText.search(/\S\s*$/)
    const afterSpace =
      lastCharIndex >= 0 ? valueText.slice(lastCharIndex + 1) : valueText
    if (afterSpace) {
      valueText = valueText.slice(0, -afterSpace.length)
    }

    let tokenIndex = node.range[0]
    /**
     * @param {string} type
     * @param {string} value
     * @returns {any}
     */
    const createToken = (type, value) => {
      /** @type {Range} */
      const range = [tokenIndex, tokenIndex + value.length]
      tokenIndex = range[1]
      /** @type {SourceLocation} */
      let loc
      return {
        type,
        value,
        range,
        get loc() {
          if (loc) {
            return loc
          }
          return (loc = {
            start: sourceCode.getLocFromIndex(range[0]),
            end: sourceCode.getLocFromIndex(range[1])
          })
        }
      }
    }

    /** @type {HTMLCommentOpen} */
    const open = createToken(TYPE_HTML_COMMENT_OPEN, '<!--')
    /** @type {HTMLCommentOpenDecoration | null} */
    const openDecoration = openDecorationText
      ? createToken(TYPE_HTML_COMMENT_OPEN_DECORATION, openDecorationText)
      : null
    tokenIndex += beforeSpace.length
    /** @type {HTMLCommentValue | null} */
    const value = valueText
      ? createToken(TYPE_HTML_COMMENT_VALUE, valueText)
      : null
    tokenIndex += afterSpace.length
    /** @type {HTMLCommentCloseDecoration | null} */
    const closeDecoration = closeDecorationText
      ? createToken(TYPE_HTML_COMMENT_CLOSE_DECORATION, closeDecorationText)
      : null
    /** @type {HTMLCommentClose} */
    const close = createToken(TYPE_HTML_COMMENT_CLOSE, '-->')

    return {
      /** HTML comment open (`<!--`) */
      open,
      /** decoration of the start of HTML comments. (`*****` when `<!--*****`) */
      openDecoration,
      /** value of HTML comment. whitespaces and other tokens are not included. */
      value,
      /** decoration of the end of HTML comments.  (`*****` when `*****-->`) */
      closeDecoration,
      /** HTML comment close (`-->`) */
      close
    }
  }
}

/**
 * Define HTML comment visitor
 *
 * @param {RuleContext} context The rule context.
 * @param {CommentParserConfig | null} config The config.
 * @param {HTMLCommentVisitor} visitHTMLComment The HTML comment visitor.
 * @param {CommentVisitorOption} [visitorOption] The option for visitor.
 * @returns {RuleListener} HTML comment visitor.
 */
function defineVisitor(context, config, visitHTMLComment, visitorOption) {
  return {
    Program(node) {
      visitorOption = visitorOption || {}
      if (utils.hasInvalidEOF(node)) {
        return
      }
      if (!node.templateBody) {
        return
      }
      const parse = defineParser(context.getSourceCode(), config)

      for (const comment of node.templateBody.comments) {
        if (comment.type !== 'HTMLComment') {
          continue
        }
        if (!visitorOption.includeDirectives && isCommentDirective(comment)) {
          // ignore directives
          continue
        }
        if (isIEConditionalComment(comment)) {
          // ignore IE conditional
          continue
        }

        const tokens = parse(comment)
        if (tokens) {
          visitHTMLComment(tokens)
        }
      }
    }
  }
}

module.exports = {
  defineVisitor
}