code:
/**
 * @fileoverview enforce Promise or callback style in `nextTick`
 * @author Flo Edelmann
 * @copyright 2020 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 {CallExpression|undefined}
 */
function getVueNextTickCallExpression(identifier, context) {
  // Instance API: this.$nextTick()
  if (
    identifier.name === '$nextTick' &&
    identifier.parent.type === 'MemberExpression' &&
    utils.isThis(identifier.parent.object, context) &&
    identifier.parent.parent.type === 'CallExpression' &&
    identifier.parent.parent.callee === identifier.parent
  ) {
    return identifier.parent.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' &&
    identifier.parent.parent.type === 'CallExpression' &&
    identifier.parent.parent.callee === identifier.parent
  ) {
    return identifier.parent.parent
  }

  // Vue 3 Global API: import { nextTick as nt } from 'vue'; nt()
  if (
    identifier.parent.type === 'CallExpression' &&
    identifier.parent.callee === identifier
  ) {
    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.parent
      }
    }
  }

  return undefined
}

/**
 * @param {CallExpression} callExpression
 * @returns {boolean}
 */
function isAwaitedPromise(callExpression) {
  return (
    callExpression.parent.type === 'AwaitExpression' ||
    (callExpression.parent.type === 'MemberExpression' &&
      callExpression.parent.property.type === 'Identifier' &&
      callExpression.parent.property.name === 'then')
  )
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'enforce Promise or callback style in `nextTick`',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/next-tick-style.html'
    },
    fixable: 'code',
    schema: [{ enum: ['promise', 'callback'] }],
    messages: {
      usePromise:
        'Use the Promise returned by `nextTick` instead of passing a callback function.',
      useCallback:
        'Pass a callback function to `nextTick` instead of using the returned Promise.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const preferredStyle =
      /** @type {string|undefined} */ (context.options[0]) || 'promise'

    return utils.defineVueVisitor(context, {
      /** @param {Identifier} node */
      Identifier(node) {
        const callExpression = getVueNextTickCallExpression(node, context)
        if (!callExpression) {
          return
        }

        if (preferredStyle === 'callback') {
          if (
            callExpression.arguments.length !== 1 ||
            isAwaitedPromise(callExpression)
          ) {
            context.report({
              node,
              messageId: 'useCallback'
            })
          }

          return
        }

        if (
          callExpression.arguments.length > 0 ||
          !isAwaitedPromise(callExpression)
        ) {
          context.report({
            node,
            messageId: 'usePromise',
            fix(fixer) {
              return fixer.insertTextAfter(node, '().then')
            }
          })
        }
      }
    })
  }
}


code:
/**
 * @author Sosuke Suzuki
 */
'use strict'

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow using arrow functions to define watcher',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-arrow-functions-in-watch.html'
    },
    fixable: null,
    schema: [],
    messages: {
      noArrowFunctionsInWatch:
        'You should not use an arrow function to define a watcher.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.executeOnVue(context, (obj) => {
      const watchNode = utils.findProperty(obj, 'watch')
      if (watchNode == null) {
        return
      }
      const watchValue = watchNode.value
      if (watchValue.type !== 'ObjectExpression') {
        return
      }
      for (const property of watchValue.properties) {
        if (property.type !== 'Property') {
          continue
        }

        for (const handler of utils.iterateWatchHandlerValues(property)) {
          if (handler.type === 'ArrowFunctionExpression') {
            context.report({
              node: handler,
              messageId: 'noArrowFunctionsInWatch'
            })
          }
        }
      }
    })
  }
}

license: mit


code:
/**
 * @fileoverview Check if there are no asynchronous actions inside computed properties.
 * @author Armano
 */
'use strict'
const { ReferenceTracker } = require('@eslint-community/eslint-utils')
const utils = require('../utils')

/**
 * @typedef {import('../utils').VueObjectData} VueObjectData
 * @typedef {import('../utils').VueVisitor} VueVisitor
 * @typedef {import('../utils').ComponentComputedProperty} ComponentComputedProperty
 */

const PROMISE_FUNCTIONS = new Set(['then', 'catch', 'finally'])

const PROMISE_METHODS = new Set(['all', 'race', 'reject', 'resolve'])

const TIMED_FUNCTIONS = new Set([
  'setTimeout',
  'setInterval',
  'setImmediate',
  'requestAnimationFrame'
])

/**
 * @param {CallExpression} node
 */
function isTimedFunction(node) {
  const callee = utils.skipChainExpression(node.callee)
  return (
    ((callee.type === 'Identifier' && TIMED_FUNCTIONS.has(callee.name)) ||
      (callee.type === 'MemberExpression' &&
        callee.object.type === 'Identifier' &&
        callee.object.name === 'window' &&
        TIMED_FUNCTIONS.has(utils.getStaticPropertyName(callee) || ''))) &&
    node.arguments.length > 0
  )
}

/**
 * @param {CallExpression} node
 */
function isPromise(node) {
  const callee = utils.skipChainExpression(node.callee)
  if (callee.type === 'MemberExpression') {
    const name = utils.getStaticPropertyName(callee)
    return (
      name &&
      // hello.PROMISE_FUNCTION()
      (PROMISE_FUNCTIONS.has(name) ||
        // Promise.PROMISE_METHOD()
        (callee.object.type === 'Identifier' &&
          callee.object.name === 'Promise' &&
          PROMISE_METHODS.has(name)))
    )
  }
  return false
}

/**
 * @param {CallExpression} node
 * @param {RuleContext} context
 */
function isNextTick(node, context) {
  const callee = utils.skipChainExpression(node.callee)
  if (callee.type === 'MemberExpression') {
    const name = utils.getStaticPropertyName(callee)
    return (
      (utils.isThis(callee.object, context) && name === '$nextTick') ||
      (callee.object.type === 'Identifier' &&
        callee.object.name === 'Vue' &&
        name === 'nextTick')
    )
  }
  return false
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow asynchronous actions in computed properties',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-async-in-computed-properties.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpectedInFunction:
        'Unexpected {{expressionName}} in computed function.',
      unexpectedInProperty:
        'Unexpected {{expressionName}} in "{{propertyName}}" computed property.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
    const computedPropertiesMap = new Map()
    /** @type {(FunctionExpression | ArrowFunctionExpression)[]} */
    const computedFunctionNodes = []

    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {BlockStatement | Expression} body
     */
    /** @type {ScopeStack | null} */
    let scopeStack = null

    const expressionTypes = {
      promise: 'asynchronous action',
      nextTick: 'asynchronous action',
      await: 'await operator',
      async: 'async function declaration',
      new: 'Promise object',
      timed: 'timed function'
    }

    /**
     * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
     * @param {VueObjectData|undefined} [info]
     */
    function onFunctionEnter(node, info) {
      if (node.async) {
        verify(
          node,
          node.body,
          'async',
          info ? computedPropertiesMap.get(info.node) : null
        )
      }

      scopeStack = {
        upper: scopeStack,
        body: node.body
      }
    }

    function onFunctionExit() {
      scopeStack = scopeStack && scopeStack.upper
    }

    /**
     * @param {ESNode} node
     * @param {BlockStatement | Expression} targetBody
     * @param {keyof expressionTypes} type
     * @param {ComponentComputedProperty[]|undefined|null} computedProperties
     */
    function verify(node, targetBody, type, computedProperties) {
      for (const cp of computedProperties || []) {
        if (
          cp.value &&
          node.loc.start.line >= cp.value.loc.start.line &&
          node.loc.end.line <= cp.value.loc.end.line &&
          targetBody === cp.value
        ) {
          context.report({
            node,
            messageId: 'unexpectedInProperty',
            data: {
              expressionName: expressionTypes[type],
              propertyName: cp.key || 'unknown'
            }
          })
          return
        }
      }

      for (const cf of computedFunctionNodes) {
        if (
          node.loc.start.line >= cf.body.loc.start.line &&
          node.loc.end.line <= cf.body.loc.end.line &&
          targetBody === cf.body
        ) {
          context.report({
            node,
            messageId: 'unexpectedInFunction',
            data: {
              expressionName: expressionTypes[type]
            }
          })
          return
        }
      }
    }
    const nodeVisitor = {
      ':function': onFunctionEnter,
      ':function:exit': onFunctionExit,

      /**
       * @param {NewExpression} node
       * @param {VueObjectData|undefined} [info]
       */
      NewExpression(node, info) {
        if (!scopeStack) {
          return
        }
        if (
          node.callee.type === 'Identifier' &&
          node.callee.name === 'Promise'
        ) {
          verify(
            node,
            scopeStack.body,
            'new',
            info ? computedPropertiesMap.get(info.node) : null
          )
        }
      },

      /**
       * @param {CallExpression} node
       * @param {VueObjectData|undefined} [info]
       */
      CallExpression(node, info) {
        if (!scopeStack) {
          return
        }
        if (isPromise(node)) {
          verify(
            node,
            scopeStack.body,
            'promise',
            info ? computedPropertiesMap.get(info.node) : null
          )
        } else if (isTimedFunction(node)) {
          verify(
            node,
            scopeStack.body,
            'timed',
            info ? computedPropertiesMap.get(info.node) : null
          )
        } else if (isNextTick(node, context)) {
          verify(
            node,
            scopeStack.body,
            'nextTick',
            info ? computedPropertiesMap.get(info.node) : null
          )
        }
      },

      /**
       * @param {AwaitExpression} node
       * @param {VueObjectData|undefined} [info]
       */
      AwaitExpression(node, info) {
        if (!scopeStack) {
          return
        }
        verify(
          node,
          scopeStack.body,
          'await',
          info ? computedPropertiesMap.get(info.node) : null
        )
      }
    }

    return utils.compositingVisitors(
      {
        /** @param {Program} program */
        Program(program) {
          const tracker = new ReferenceTracker(utils.getScope(context, program))
          for (const { node } of utils.iterateReferencesTraceMap(tracker, {
            computed: {
              [ReferenceTracker.CALL]: true
            }
          })) {
            if (node.type !== 'CallExpression') {
              continue
            }

            const getter = utils.getGetterBodyFromComputedFunction(node)
            if (getter) {
              computedFunctionNodes.push(getter)
            }
          }
        }
      },
      utils.isScriptSetup(context)
        ? utils.defineScriptSetupVisitor(context, nodeVisitor)
        : utils.defineVueVisitor(context, {
            onVueObjectEnter(node) {
              computedPropertiesMap.set(node, utils.getComputedProperties(node))
            },
            ...nodeVisitor
          })
    )
  }
}


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

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

/**
 * @typedef { { names: { [tagName in string]: Set<string> }, regexps: { name: RegExp, attrs: Set<string> }[], cache: { [tagName in string]: Set<string> } } } TargetAttrs
 */

// https://dev.w3.org/html5/html-author/charref
const DEFAULT_ALLOWLIST = [
  '(',
  ')',
  ',',
  '.',
  '&',
  '+',
  '-',
  '=',
  '*',
  '/',
  '#',
  '%',
  '!',
  '?',
  ':',
  '[',
  ']',
  '{',
  '}',
  '<',
  '>',
  '\u00B7', // "·"
  '\u2022', // "•"
  '\u2010', // "‐"
  '\u2013', // "–"
  '\u2014', // "—"
  '\u2212', // "−"
  '|'
]

const DEFAULT_ATTRIBUTES = {
  '/.+/': [
    'title',
    'aria-label',
    'aria-placeholder',
    'aria-roledescription',
    'aria-valuetext'
  ],
  input: ['placeholder'],
  img: ['alt']
}

const DEFAULT_DIRECTIVES = ['v-text']

/**
 * Parse attributes option
 * @param {any} options
 * @returns {TargetAttrs}
 */
function parseTargetAttrs(options) {
  /** @type {TargetAttrs} */
  const result = { names: {}, regexps: [], cache: {} }
  for (const tagName of Object.keys(options)) {
    /** @type { Set<string> } */
    const attrs = new Set(options[tagName])
    if (regexp.isRegExp(tagName)) {
      result.regexps.push({
        name: regexp.toRegExp(tagName),
        attrs
      })
    } else {
      result.names[tagName] = attrs
    }
  }
  return result
}

/**
 * Get a string from given expression container node
 * @param {VExpressionContainer} value
 * @returns { string | null }
 */
function getStringValue(value) {
  const expression = value.expression
  if (!expression) {
    return null
  }
  if (expression.type !== 'Literal') {
    return null
  }
  if (typeof expression.value === 'string') {
    return expression.value
  }
  return null
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow the use of bare strings in `<template>`',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-bare-strings-in-template.html'
    },
    schema: [
      {
        type: 'object',
        properties: {
          allowlist: {
            type: 'array',
            items: { type: 'string' },
            uniqueItems: true
          },
          attributes: {
            type: 'object',
            patternProperties: {
              '^(?:\\S+|/.*/[a-z]*)$': {
                type: 'array',
                items: { type: 'string' },
                uniqueItems: true
              }
            },
            additionalProperties: false
          },
          directives: {
            type: 'array',
            items: { type: 'string', pattern: '^v-' },
            uniqueItems: true
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      unexpected: 'Unexpected non-translated string used.',
      unexpectedInAttr: 'Unexpected non-translated string used in `{{attr}}`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @typedef { { upper: ElementStack | null, name: string, attrs: Set<string> } } ElementStack
     */
    const opts = context.options[0] || {}
    /** @type {string[]} */
    const allowlist = opts.allowlist || DEFAULT_ALLOWLIST
    const attributes = parseTargetAttrs(opts.attributes || DEFAULT_ATTRIBUTES)
    const directives = opts.directives || DEFAULT_DIRECTIVES

    const allowlistRe = new RegExp(
      allowlist.map((w) => regexp.escape(w)).join('|'),
      'gu'
    )

    /** @type {ElementStack | null} */
    let elementStack = null
    /**
     * Gets the bare string from given string
     * @param {string} str
     */
    function getBareString(str) {
      return str.trim().replace(allowlistRe, '').trim()
    }

    /**
     * Get the attribute to be verified from the element name.
     * @param {string} tagName
     * @returns {Set<string>}
     */
    function getTargetAttrs(tagName) {
      if (attributes.cache[tagName]) {
        return attributes.cache[tagName]
      }
      /** @type {string[]} */
      const result = []
      if (attributes.names[tagName]) {
        result.push(...attributes.names[tagName])
      }
      for (const { name, attrs } of attributes.regexps) {
        name.lastIndex = 0
        if (name.test(tagName)) {
          result.push(...attrs)
        }
      }
      if (casing.isKebabCase(tagName)) {
        result.push(...getTargetAttrs(casing.pascalCase(tagName)))
      }

      return (attributes.cache[tagName] = new Set(result))
    }

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VText} node */
      VText(node) {
        if (getBareString(node.value)) {
          context.report({
            node,
            messageId: 'unexpected'
          })
        }
      },
      /**
       * @param {VElement} node
       */
      VElement(node) {
        elementStack = {
          upper: elementStack,
          name: node.rawName,
          attrs: getTargetAttrs(node.rawName)
        }
      },
      'VElement:exit'() {
        elementStack = elementStack && elementStack.upper
      },
      /** @param {VAttribute|VDirective} node */
      VAttribute(node) {
        if (!node.value || !elementStack) {
          return
        }
        if (node.directive === false) {
          const attrs = elementStack.attrs
          if (!attrs.has(node.key.rawName)) {
            return
          }

          if (getBareString(node.value.value)) {
            context.report({
              node: node.value,
              messageId: 'unexpectedInAttr',
              data: {
                attr: node.key.rawName
              }
            })
          }
        } else {
          const directive = `v-${node.key.name.name}`
          if (!directives.includes(directive)) {
            return
          }
          const str = getStringValue(node.value)
          if (str && getBareString(str)) {
            context.report({
              node: node.value,
              messageId: 'unexpectedInAttr',
              data: {
                attr: directive
              }
            })
          }
        }
      }
    })
  }
}


code:
/**
 * @fileoverview Prevents boolean defaults from being set
 * @author Hiroki Osame
 */
'use strict'

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

/**
 * @typedef {import('../utils').ComponentProp} ComponentProp
 * @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
 */

/**
 * @param {Expression|undefined} node
 */
function isBooleanIdentifier(node) {
  return Boolean(node && node.type === 'Identifier' && node.name === 'Boolean')
}

/**
 * Detects whether given prop node is a Boolean
 * @param {ComponentObjectProp} prop
 * @return {Boolean}
 */
function isBooleanProp(prop) {
  const value = utils.skipTSAsExpression(prop.value)
  return (
    isBooleanIdentifier(value) ||
    (value.type === 'ObjectExpression' &&
      isBooleanIdentifier(utils.findProperty(value, 'type')?.value))
  )
}

/**
 * @param {ObjectExpression} propDefValue
 */
function getDefaultNode(propDefValue) {
  return utils.findProperty(propDefValue, 'default')
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow boolean defaults',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-boolean-default.html'
    },
    fixable: null,
    schema: [
      {
        enum: ['default-false', 'no-default']
      }
    ],
    messages: {
      noBooleanDefault:
        'Boolean prop should not set a default (Vue defaults it to false).',
      defaultFalse: 'Boolean prop should only be defaulted to false.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const booleanType = context.options[0] || 'no-default'
    /**
     * @param {ComponentProp} prop
     * @param {(propName: string) => Expression[]} otherDefaultProvider
     */
    function processProp(prop, otherDefaultProvider) {
      if (prop.type === 'object') {
        if (!isBooleanProp(prop)) {
          return
        }
        if (prop.value.type === 'ObjectExpression') {
          const defaultNode = getDefaultNode(prop.value)
          if (defaultNode) {
            verifyDefaultExpression(defaultNode.value)
          }
        }
        if (prop.propName != null) {
          for (const defaultNode of otherDefaultProvider(prop.propName)) {
            verifyDefaultExpression(defaultNode)
          }
        }
      } else if (prop.type === 'type') {
        if (prop.types.length !== 1 || prop.types[0] !== 'Boolean') {
          return
        }
        for (const defaultNode of otherDefaultProvider(prop.propName)) {
          verifyDefaultExpression(defaultNode)
        }
      }
    }
    /**
     * @param {ComponentProp[]} props
     * @param {(propName: string) => Expression[]} otherDefaultProvider
     */
    function processProps(props, otherDefaultProvider) {
      for (const prop of props) {
        processProp(prop, otherDefaultProvider)
      }
    }

    /**
     * @param {Expression} defaultNode
     */
    function verifyDefaultExpression(defaultNode) {
      switch (booleanType) {
        case 'no-default': {
          context.report({
            node: defaultNode,
            messageId: 'noBooleanDefault'
          })
          break
        }

        case 'default-false': {
          if (defaultNode.type !== 'Literal' || defaultNode.value !== false) {
            context.report({
              node: defaultNode,
              messageId: 'defaultFalse'
            })
          }
          break
        }
      }
    }
    return utils.compositingVisitors(
      utils.executeOnVueComponent(context, (obj) => {
        processProps(utils.getComponentPropsFromOptions(obj), () => [])
      }),
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(node, props) {
          const defaultsByWithDefaults =
            utils.getWithDefaultsPropExpressions(node)
          const defaultsByAssignmentPatterns =
            utils.getDefaultPropExpressionsForPropsDestructure(node)
          processProps(props, (propName) =>
            [
              defaultsByWithDefaults[propName],
              defaultsByAssignmentPatterns[propName]?.expression
            ].filter(utils.isDef)
          )
        }
      })
    )
  }
}


code:
/**
 * @author Flo Edelmann
 * See LICENSE file in root directory for full license.
 */
'use strict'
const { defineTemplateBodyVisitor } = require('../utils')

/**
 * @typedef {object} RuleOption
 * @property {string[]} additionalDirectives
 */

/**
 * @param {VNode | Token} node
 * @returns {boolean}
 */
function isWhiteSpaceTextNode(node) {
  return node.type === 'VText' && node.value.trim() === ''
}

/**
 * @param {Position} pos1
 * @param {Position} pos2
 * @returns {'less' | 'equal' | 'greater'}
 */
function comparePositions(pos1, pos2) {
  if (
    pos1.line < pos2.line ||
    (pos1.line === pos2.line && pos1.column < pos2.column)
  ) {
    return 'less'
  }

  if (
    pos1.line > pos2.line ||
    (pos1.line === pos2.line && pos1.column > pos2.column)
  ) {
    return 'greater'
  }

  return 'equal'
}

/**
 * @param {(VNode | Token)[]} nodes
 * @returns {SourceLocation | undefined}
 */
function getLocationRange(nodes) {
  /** @type {Position | undefined} */
  let start
  /** @type {Position | undefined} */
  let end

  for (const node of nodes) {
    if (!start || comparePositions(node.loc.start, start) === 'less') {
      start = node.loc.start
    }

    if (!end || comparePositions(node.loc.end, end) === 'greater') {
      end = node.loc.end
    }
  }

  if (start === undefined || end === undefined) {
    return undefined
  }

  return { start, end }
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        "disallow element's child contents which would be overwritten by a directive like `v-html` or `v-text`",
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-child-content.html'
    },
    fixable: null,
    hasSuggestions: true,
    schema: [
      {
        type: 'object',
        additionalProperties: false,
        properties: {
          additionalDirectives: {
            type: 'array',
            uniqueItems: true,
            minItems: 1,
            items: {
              type: 'string'
            }
          }
        },
        required: ['additionalDirectives']
      }
    ],
    messages: {
      disallowedChildContent:
        'Child content is disallowed because it will be overwritten by the v-{{ directiveName }} directive.',
      removeChildContent: 'Remove child content.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const directives = new Set(['html', 'text'])

    /** @type {RuleOption | undefined} */
    const option = context.options[0]
    if (option !== undefined) {
      for (const directive of option.additionalDirectives) {
        directives.add(directive)
      }
    }

    return defineTemplateBodyVisitor(context, {
      /** @param {VDirective} directiveNode */
      'VAttribute[directive=true]'(directiveNode) {
        const directiveName = directiveNode.key.name.name
        const elementNode = directiveNode.parent.parent

        if (elementNode.endTag === null) {
          return
        }
        const sourceCode = context.getSourceCode()
        const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
        const elementComments = tokenStore.getTokensBetween(
          elementNode.startTag,
          elementNode.endTag,
          {
            includeComments: true,
            filter: (token) => token.type === 'HTMLComment'
          }
        )

        const childNodes = [...elementNode.children, ...elementComments]

        if (
          directives.has(directiveName) &&
          childNodes.some((childNode) => !isWhiteSpaceTextNode(childNode))
        ) {
          context.report({
            node: elementNode,
            loc: getLocationRange(childNodes),
            messageId: 'disallowedChildContent',
            data: { directiveName },
            suggest: [
              {
                messageId: 'removeChildContent',
                *fix(fixer) {
                  for (const childNode of childNodes) {
                    yield fixer.remove(childNode)
                  }
                }
              }
            ]
          })
        }
      }
    })
  }
}


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

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

/**
 * @typedef {import('../utils').VueObjectData} VueObjectData
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow accessing computed properties in `data`',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-computed-properties-in-data.html'
    },
    fixable: null,
    schema: [],
    messages: {
      cannotBeUsed:
        'The computed property cannot be used in `data()` because it is before initialization.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {Map<ObjectExpression, {data: FunctionExpression | ArrowFunctionExpression, computedNames:Set<string>}>} */
    const contextMap = new Map()

    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
     */
    /** @type {ScopeStack | null} */
    let scopeStack = null

    return utils.compositingVisitors(
      {
        /**
         * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
         */
        ':function'(node) {
          scopeStack = {
            upper: scopeStack,
            node
          }
        },
        ':function:exit'() {
          scopeStack = scopeStack && scopeStack.upper
        }
      },
      utils.defineVueVisitor(context, {
        onVueObjectEnter(node) {
          const dataProperty = utils.findProperty(node, 'data')
          if (
            !dataProperty ||
            (dataProperty.value.type !== 'FunctionExpression' &&
              dataProperty.value.type !== 'ArrowFunctionExpression')
          ) {
            return
          }
          const computedNames = new Set()
          for (const computed of utils.iterateProperties(
            node,
            new Set(['computed'])
          )) {
            computedNames.add(computed.name)
          }

          contextMap.set(node, { data: dataProperty.value, computedNames })
        },
        /**
         * @param {MemberExpression} node
         * @param {VueObjectData} vueData
         */
        MemberExpression(node, vueData) {
          if (!scopeStack || !utils.isThis(node.object, context)) {
            return
          }
          const ctx = contextMap.get(vueData.node)
          if (!ctx || ctx.data !== scopeStack.node) {
            return
          }
          const name = utils.getStaticPropertyName(node)
          if (!name || !ctx.computedNames.has(name)) {
            return
          }
          context.report({
            node,
            messageId: 'cannotBeUsed'
          })
        }
      })
    )
  }
}


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

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

// eslint-disable-next-line internal/no-invalid-meta
module.exports = utils.wrapCoreRule('no-console', {
  skipBaseHandlers: true,
  create(context) {
    const options = context.options[0] || {}
    const allowed = options.allow || []

    /**
     * Copied from the core rule `no-console`.
     * Checks whether the property name of the given MemberExpression node
     * is allowed by options or not.
     * @param {MemberExpression} node The MemberExpression node to check.
     * @returns {boolean} `true` if the property name of the node is allowed.
     */
    function isAllowed(node) {
      const propertyName = utils.getStaticPropertyName(node)

      return propertyName && allowed.includes(propertyName)
    }

    return {
      MemberExpression(node) {
        if (
          node.object.type === 'Identifier' &&
          node.object.name === 'console' &&
          !isAllowed(node)
        ) {
          context.report({
            node: node.object,
            loc: node.object.loc,
            messageId: 'unexpected'
          })
        }
      }
    }
  }
})


code:
/**
 * @author Flo Edelmann
 */
'use strict'

const { wrapCoreRule } = require('../utils')

const conditionalDirectiveNames = new Set(['v-show', 'v-if', 'v-else-if'])

// eslint-disable-next-line internal/no-invalid-meta
module.exports = wrapCoreRule('no-constant-condition', {
  create(_context, { baseHandlers }) {
    return {
      VDirectiveKey(node) {
        if (
          conditionalDirectiveNames.has(`v-${node.name.name}`) &&
          node.parent.value &&
          node.parent.value.expression &&
          baseHandlers.IfStatement
        ) {
          baseHandlers.IfStatement({
            // @ts-expect-error -- Process expression of VExpressionContainer as IfStatement.
            test: node.parent.value.expression
          })
        }
      }
    }
  }
})


code:
/**
 * @author Przemyslaw Falowski (@przemkow)
 * @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
 */
'use strict'

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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow custom modifiers on v-model used on the component',
      categories: ['vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-custom-modifiers-on-v-model.html'
    },
    fixable: null,
    schema: [],
    messages: {
      notSupportedModifier:
        "'v-model' directives don't support the modifier '{{name}}'."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      "VAttribute[directive=true][key.name.name='model']"(node) {
        const element = node.parent.parent

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


code:
/**
 * @fileoverview disallow using deprecated object declaration on data
 * @author yoyo930021
 */
'use strict'

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

/** @param {Token} token */
function isOpenParen(token) {
  return token.type === 'Punctuator' && token.value === '('
}

/** @param {Token} token */
function isCloseParen(token) {
  return token.type === 'Punctuator' && token.value === ')'
}

/**
 * @param {Expression} node
 * @param {SourceCode} sourceCode
 */
function getFirstAndLastTokens(node, sourceCode) {
  let first = sourceCode.getFirstToken(node)
  let last = sourceCode.getLastToken(node)

  // If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses.
  while (true) {
    const prev = sourceCode.getTokenBefore(first)
    const next = sourceCode.getTokenAfter(last)
    if (isOpenParen(prev) && isCloseParen(next)) {
      first = prev
      last = next
    } else {
      return { first, last }
    }
  }
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated object declaration on data (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-data-object-declaration.html'
    },
    fixable: 'code',
    schema: [],
    messages: {
      objectDeclarationIsDeprecated:
        "Object declaration on 'data' property is deprecated. Using function declaration instead."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const sourceCode = context.getSourceCode()

    return utils.executeOnVue(context, (obj) => {
      const invalidData = utils.findProperty(
        obj,
        'data',
        (p) =>
          p.value.type !== 'FunctionExpression' &&
          p.value.type !== 'ArrowFunctionExpression' &&
          p.value.type !== 'Identifier'
      )

      if (invalidData) {
        context.report({
          node: invalidData,
          messageId: 'objectDeclarationIsDeprecated',
          fix(fixer) {
            const tokens = getFirstAndLastTokens(invalidData.value, sourceCode)

            return [
              fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
              fixer.insertTextAfter(tokens.last, ';\n}')
            ]
          }
        })
      }
    })
  }
}


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

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

/**
 * @typedef {import('@eslint-community/eslint-utils').TYPES.TraceMap} TraceMap
 */

/** @type {TraceMap} */
const deletedImportApisMap = {
  set: {
    [ReferenceTracker.CALL]: true
  },
  del: {
    [ReferenceTracker.CALL]: true
  }
}
const deprecatedApis = new Set(['set', 'delete'])
const deprecatedDollarApis = new Set(['$set', '$delete'])

/**
 * @param {Expression|Super} node
 */
function isVue(node) {
  return node.type === 'Identifier' && node.name === 'Vue'
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-deprecated-delete-set.html'
    },
    fixable: null,
    schema: [],
    messages: {
      deprecated: 'The `$delete`, `$set` is deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @param {Identifier} identifier
     * @param {RuleContext} context
     * @returns {CallExpression|undefined}
     */
    function getVueDeprecatedCallExpression(identifier, context) {
      // Instance API: this.$set()
      if (
        deprecatedDollarApis.has(identifier.name) &&
        identifier.parent.type === 'MemberExpression' &&
        utils.isThis(identifier.parent.object, context) &&
        identifier.parent.parent.type === 'CallExpression' &&
        identifier.parent.parent.callee === identifier.parent
      ) {
        return identifier.parent.parent
      }

      // Vue 2 Global API: Vue.set()
      if (
        deprecatedApis.has(identifier.name) &&
        identifier.parent.type === 'MemberExpression' &&
        isVue(identifier.parent.object) &&
        identifier.parent.parent.type === 'CallExpression' &&
        identifier.parent.parent.callee === identifier.parent
      ) {
        return identifier.parent.parent
      }

      return undefined
    }

    const nodeVisitor = {
      /** @param {Identifier} node */
      Identifier(node) {
        const callExpression = getVueDeprecatedCallExpression(node, context)
        if (!callExpression) {
          return
        }

        context.report({
          node,
          messageId: 'deprecated'
        })
      }
    }

    return utils.compositingVisitors(
      utils.defineVueVisitor(context, nodeVisitor),
      utils.defineScriptSetupVisitor(context, nodeVisitor),
      {
        /** @param {Program} node */
        Program(node) {
          const tracker = new ReferenceTracker(utils.getScope(context, node))

          // import { set } from 'vue'; set()
          const esmTraceMap = {
            vue: {
              [ReferenceTracker.ESM]: true,
              ...deletedImportApisMap
            }
          }
          // const { set } = require('vue'); set()
          const cjsTraceMap = {
            vue: {
              ...deletedImportApisMap
            }
          }

          for (const { node } of [
            ...tracker.iterateEsmReferences(esmTraceMap),
            ...tracker.iterateCjsReferences(cjsTraceMap)
          ]) {
            const refNode = /** @type {CallExpression} */ (node)
            context.report({
              node: refNode.callee,
              messageId: 'deprecated'
            })
          }
        }
      }
    )
  }
}


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

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

/**
 * @param {RuleFixer} fixer
 * @param {Property} property
 * @param {string} newName
 */
function fix(fixer, property, newName) {
  if (property.computed) {
    if (
      property.key.type === 'Literal' ||
      property.key.type === 'TemplateLiteral'
    ) {
      return fixer.replaceTextRange(
        [property.key.range[0] + 1, property.key.range[1] - 1],
        newName
      )
    }
    return null
  }
  if (property.shorthand) {
    return fixer.insertTextBefore(property.key, `${newName}:`)
  }
  return fixer.replaceText(property.key, newName)
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-destroyed-lifecycle.html'
    },
    fixable: 'code',
    schema: [],
    messages: {
      deprecatedDestroyed:
        'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
      deprecatedBeforeDestroy:
        'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.executeOnVue(context, (obj) => {
      const destroyed = utils.findProperty(obj, 'destroyed')

      if (destroyed) {
        context.report({
          node: destroyed.key,
          messageId: 'deprecatedDestroyed',
          fix(fixer) {
            return fix(fixer, destroyed, 'unmounted')
          }
        })
      }

      const beforeDestroy = utils.findProperty(obj, 'beforeDestroy')
      if (beforeDestroy) {
        context.report({
          node: beforeDestroy.key,
          messageId: 'deprecatedBeforeDestroy',
          fix(fixer) {
            return fix(fixer, beforeDestroy, 'beforeUnmount')
          }
        })
      }
    })
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow using deprecated `$listeners` (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-dollar-listeners-api.html'
    },
    fixable: null,
    schema: [],
    messages: {
      deprecated: 'The `$listeners` is deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(
      context,
      {
        VExpressionContainer(node) {
          for (const reference of node.references) {
            if (reference.variable != null) {
              // Not vm reference
              continue
            }
            if (reference.id.name === '$listeners') {
              context.report({
                node: reference.id,
                messageId: 'deprecated'
              })
            }
          }
        }
      },
      utils.defineVueVisitor(context, {
        MemberExpression(node) {
          if (
            node.property.type !== 'Identifier' ||
            node.property.name !== '$listeners'
          ) {
            return
          }
          if (!utils.isThis(node.object, context)) {
            return
          }

          context.report({
            node: node.property,
            messageId: 'deprecated'
          })
        }
      })
    )
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-dollar-scopedslots-api.html'
    },
    fixable: 'code',
    schema: [],
    messages: {
      deprecated: 'The `$scopedSlots` is deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(
      context,
      {
        VExpressionContainer(node) {
          for (const reference of node.references) {
            if (reference.variable != null) {
              // Not vm reference
              continue
            }
            if (reference.id.name === '$scopedSlots') {
              context.report({
                node: reference.id,
                messageId: 'deprecated',
                fix(fixer) {
                  return fixer.replaceText(reference.id, '$slots')
                }
              })
            }
          }
        }
      },
      utils.defineVueVisitor(context, {
        MemberExpression(node) {
          if (
            node.property.type !== 'Identifier' ||
            node.property.name !== '$scopedSlots'
          ) {
            return
          }
          if (!utils.isThis(node.object, context)) {
            return
          }

          context.report({
            node: node.property,
            messageId: 'deprecated',
            fix(fixer) {
              return fixer.replaceText(node.property, '$slots')
            }
          })
        }
      })
    )
  }
}


code:
/**
 * @fileoverview disallow using deprecated events api
 * @author yoyo930021
 */
'use strict'

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow using deprecated events api (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-events-api.html'
    },
    fixable: null,
    schema: [],
    messages: {
      noDeprecatedEventsApi:
        'The Events api `$on`, `$off` `$once` is deprecated. Using external library instead, for example mitt.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineVueVisitor(context, {
      /** @param {MemberExpression & ({parent: CallExpression} | {parent: ChainExpression & {parent: CallExpression}})} node */
      'CallExpression > MemberExpression, CallExpression > ChainExpression > MemberExpression'(
        node
      ) {
        const call =
          node.parent.type === 'ChainExpression'
            ? node.parent.parent
            : node.parent

        if (call.optional) {
          // It is OK because checking whether it is deprecated.
          // e.g. `this.$on?.()`
          return
        }

        if (
          utils.skipChainExpression(call.callee) !== node ||
          !['$on', '$off', '$once'].includes(
            utils.getStaticPropertyName(node) || ''
          )
        ) {
          return
        }
        if (!utils.isThis(node.object, context)) {
          return
        }

        context.report({
          node: node.property,
          messageId: 'noDeprecatedEventsApi'
        })
      }
    })
  }
}


code:
/**
 * @author Przemyslaw Falowski (@przemkow)
 * @fileoverview disallow using deprecated filters syntax
 */
'use strict'

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated filters syntax (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-filter.html'
    },
    fixable: null,
    schema: [],
    messages: {
      noDeprecatedFilter: 'Filters are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      VFilterSequenceExpression(node) {
        context.report({
          node,
          loc: node.loc,
          messageId: 'noDeprecatedFilter'
        })
      }
    })
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated the `functional` template (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-functional-template.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: 'The `functional` template are deprecated.'
    }
  },
  /**
   * @param {RuleContext} context - The rule context.
   * @returns {RuleListener} AST event handlers.
   */
  create(context) {
    return {
      Program(program) {
        const element = program.templateBody
        if (element == null) {
          return
        }

        const functional = utils.getAttribute(element, 'functional')

        if (functional) {
          context.report({
            node: functional,
            messageId: 'unexpected'
          })
        }
      }
    }
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-html-element-is.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: 'The `is` attribute on HTML element are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @param {VElement} node */
    function isValidElement(node) {
      return (
        !utils.isHtmlWellKnownElementName(node.rawName) &&
        !utils.isSvgWellKnownElementName(node.rawName) &&
        !utils.isMathWellKnownElementName(node.rawName)
      )
    }
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirective} node */
      "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is']"(
        node
      ) {
        if (isValidElement(node.parent.parent)) {
          return
        }
        context.report({
          node,
          loc: node.loc,
          messageId: 'unexpected'
        })
      },
      /** @param {VAttribute} node */
      "VAttribute[directive=false][key.name='is']"(node) {
        if (isValidElement(node.parent.parent)) {
          return
        }
        if (node.value && node.value.value.startsWith('vue:')) {
          // Usage on native elements 3.1+
          return
        }
        context.report({
          node,
          loc: node.loc,
          messageId: 'unexpected'
        })
      }
    })
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `inline-template` attribute (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-inline-template.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: '`inline-template` are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VIdentifier} node */
      "VAttribute[directive=false] > VIdentifier[rawName='inline-template']"(
        node
      ) {
        context.report({
          node,
          loc: node.loc,
          messageId: 'unexpected'
        })
      }
    })
  }
}


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

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

const allowedPropNames = new Set(['modelValue', 'model-value'])
const allowedEventNames = new Set(['update:modelValue', 'update:model-value'])

/**
 * @param {ObjectExpression} node
 * @param {string} key
 * @returns {Literal | TemplateLiteral | undefined}
 */
function findPropertyValue(node, key) {
  const property = node.properties.find(
    (property) =>
      property.type === 'Property' &&
      property.key.type === 'Identifier' &&
      property.key.name === key
  )
  if (
    !property ||
    property.type !== 'Property' ||
    !utils.isStringLiteral(property.value)
  ) {
    return undefined
  }
  return property.value
}

/**
 * @param {RuleFixer} fixer
 * @param {Literal | TemplateLiteral} node
 * @param {string} text
 */
function replaceLiteral(fixer, node, text) {
  return fixer.replaceTextRange([node.range[0] + 1, node.range[1] - 1], text)
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow deprecated `model` definition (in Vue.js 3.0.0+)',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-deprecated-model-definition.html'
    },
    fixable: null,
    hasSuggestions: true,
    schema: [
      {
        type: 'object',
        additionalProperties: false,
        properties: {
          allowVue3Compat: {
            type: 'boolean'
          }
        }
      }
    ],
    messages: {
      deprecatedModel: '`model` definition is deprecated.',
      vue3Compat:
        '`model` definition is deprecated. You may use the Vue 3-compatible `modelValue`/`update:modelValue` though.',
      changeToModelValue: 'Change to `modelValue`/`update:modelValue`.',
      changeToKebabModelValue: 'Change to `model-value`/`update:model-value`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const allowVue3Compat = Boolean(context.options[0]?.allowVue3Compat)

    return utils.executeOnVue(context, (obj) => {
      const modelProperty = utils.findProperty(obj, 'model')
      if (!modelProperty || modelProperty.value.type !== 'ObjectExpression') {
        return
      }

      if (!allowVue3Compat) {
        context.report({
          node: modelProperty,
          messageId: 'deprecatedModel'
        })
        return
      }

      const propName = findPropertyValue(modelProperty.value, 'prop')
      const eventName = findPropertyValue(modelProperty.value, 'event')

      if (
        !propName ||
        !eventName ||
        !allowedPropNames.has(
          utils.getStringLiteralValue(propName, true) ?? ''
        ) ||
        !allowedEventNames.has(
          utils.getStringLiteralValue(eventName, true) ?? ''
        )
      ) {
        context.report({
          node: modelProperty,
          messageId: 'vue3Compat',
          suggest:
            propName && eventName
              ? [
                  {
                    messageId: 'changeToModelValue',
                    *fix(fixer) {
                      const newPropName = 'modelValue'
                      const newEventName = 'update:modelValue'
                      yield replaceLiteral(fixer, propName, newPropName)
                      yield replaceLiteral(fixer, eventName, newEventName)
                    }
                  },
                  {
                    messageId: 'changeToKebabModelValue',
                    *fix(fixer) {
                      const newPropName = 'model-value'
                      const newEventName = 'update:model-value'
                      yield replaceLiteral(fixer, propName, newPropName)
                      yield replaceLiteral(fixer, eventName, newEventName)
                    }
                  }
                ]
              : []
        })
      }
    })
  }
}


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

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

/**
 * @param {Expression|SpreadElement|null} node
 */
function isFunctionIdentifier(node) {
  return node && node.type === 'Identifier' && node.name === 'Function'
}

/**
 * @param {Expression} node
 * @returns {boolean}
 */
function hasFunctionType(node) {
  if (isFunctionIdentifier(node)) {
    return true
  }
  if (node.type === 'ArrayExpression') {
    return node.elements.some(isFunctionIdentifier)
  }
  return false
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow deprecated `this` access in props default function (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-props-default-this.html'
    },
    fixable: null,
    schema: [],
    messages: {
      deprecated:
        'Props default value factory functions no longer have access to `this`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {FunctionExpression | FunctionDeclaration} node
     * @property {boolean} propDefault
     */
    /** @type {Set<FunctionExpression>} */
    const propsDefault = new Set()
    /** @type {ScopeStack | null} */
    let scopeStack = null

    /**
     * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
     */
    function onFunctionEnter(node) {
      if (node.type === 'ArrowFunctionExpression') {
        return
      }
      if (scopeStack) {
        scopeStack = {
          upper: scopeStack,
          node,
          propDefault: false
        }
      } else if (node.type === 'FunctionExpression' && propsDefault.has(node)) {
        scopeStack = {
          upper: scopeStack,
          node,
          propDefault: true
        }
      }
    }

    /**
     * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
     */
    function onFunctionExit(node) {
      if (scopeStack && scopeStack.node === node) {
        scopeStack = scopeStack.upper
      }
    }

    return utils.defineVueVisitor(context, {
      onVueObjectEnter(node) {
        for (const prop of utils.getComponentPropsFromOptions(node)) {
          if (prop.type !== 'object') {
            continue
          }
          if (prop.value.type !== 'ObjectExpression') {
            continue
          }
          const def = utils.findProperty(prop.value, 'default')
          if (!def) {
            continue
          }
          const type = utils.findProperty(prop.value, 'type')
          if (type && hasFunctionType(type.value)) {
            // ignore function type
            continue
          }
          if (def.value.type !== 'FunctionExpression') {
            continue
          }
          propsDefault.add(def.value)
        }
      },
      ':function': onFunctionEnter,
      ':function:exit': onFunctionExit,
      ThisExpression(node) {
        if (scopeStack && scopeStack.propDefault) {
          context.report({
            node,
            messageId: 'deprecated'
          })
        }
      }
    })
  }
}


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

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

/** @param {RuleContext} context */
function getComponentNames(context) {
  let components = ['RouterLink']

  if (context.options[0] && context.options[0].components) {
    components = context.options[0].components
  }

  return new Set(
    components.flatMap((component) => [
      casing.kebabCase(component),
      casing.pascalCase(component)
    ])
  )
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-router-link-tag-prop.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          components: {
            type: 'array',
            items: {
              type: 'string'
            },
            uniqueItems: true,
            minItems: 1
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      deprecated:
        "'tag' property on '{{element}}' component is deprecated. Use scoped slots instead."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const components = getComponentNames(context)

    return utils.defineTemplateBodyVisitor(context, {
      VElement(node) {
        if (!components.has(node.rawName)) return

        /** @type VIdentifier | null */
        let tagKey = null

        const tagAttr = utils.getAttribute(node, 'tag')
        if (tagAttr) {
          tagKey = tagAttr.key
        } else {
          const directive = utils.getDirective(node, 'bind', 'tag')
          if (directive) {
            const arg = directive.key.argument
            if (arg && arg.type === 'VIdentifier') {
              tagKey = arg
            }
          }
        }

        if (tagKey) {
          context.report({
            node: tagKey,
            messageId: 'deprecated',
            data: {
              element: node.rawName
            }
          })
        }
      }
    })
  }
}


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

const utils = require('../utils')
const scopeAttribute = require('./syntaxes/scope-attribute')

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow deprecated `scope` attribute (in Vue.js 2.5.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-scope-attribute.html'
    },
    // eslint-disable-next-line eslint-plugin/require-meta-fixable -- fixer is not recognized
    fixable: 'code',
    schema: [],
    messages: {
      forbiddenScopeAttribute: '`scope` attributes are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const templateBodyVisitor =
      scopeAttribute.createTemplateBodyVisitor(context)
    return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
  }
}


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

const utils = require('../utils')
const slotAttribute = require('./syntaxes/slot-attribute')

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow deprecated `slot` attribute (in Vue.js 2.6.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-slot-attribute.html'
    },
    // eslint-disable-next-line eslint-plugin/require-meta-fixable -- fixer is not recognized
    fixable: 'code',
    schema: [
      {
        type: 'object',
        properties: {
          ignore: {
            type: 'array',
            items: { type: 'string' },
            uniqueItems: true
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      forbiddenSlotAttribute: '`slot` attributes are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const templateBodyVisitor = slotAttribute.createTemplateBodyVisitor(context)
    return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
  }
}


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

const utils = require('../utils')
const slotScopeAttribute = require('./syntaxes/slot-scope-attribute')

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-slot-scope-attribute.html'
    },
    // eslint-disable-next-line eslint-plugin/require-meta-fixable -- fixer is not recognized
    fixable: 'code',
    schema: [],
    messages: {
      forbiddenSlotScopeAttribute: '`slot-scope` are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const templateBodyVisitor = slotScopeAttribute.createTemplateBodyVisitor(
      context,
      { fixToUpgrade: true }
    )
    return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
  }
}


code:
/**
 * @author Przemyslaw Falowski (@przemkow)
 * @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
 */
'use strict'

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-bind-sync.html'
    },
    fixable: 'code',
    schema: [],
    messages: {
      syncModifierIsDeprecated:
        "'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      "VAttribute[directive=true][key.name.name='bind']"(node) {
        if (node.key.modifiers.map((mod) => mod.name).includes('sync')) {
          context.report({
            node,
            loc: node.loc,
            messageId: 'syncModifierIsDeprecated',
            fix(fixer) {
              if (node.key.argument == null) {
                // is using spread syntax
                return null
              }
              if (node.key.modifiers.length > 1) {
                // has multiple modifiers
                return null
              }

              const bindArgument = context
                .getSourceCode()
                .getText(node.key.argument)
              return fixer.replaceText(node.key, `v-model:${bindArgument}`)
            }
          })
        }
      }
    })
  }
}


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

const utils = require('../utils')
const vIs = require('./syntaxes/v-is')

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow deprecated `v-is` directive (in Vue.js 3.1.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-is.html'
    },
    fixable: null,
    schema: [],
    messages: {
      forbiddenVIs: '`v-is` directive is deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const templateBodyVisitor = vIs.createTemplateBodyVisitor(context)
    return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `.native` modifiers (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-on-native-modifier.html'
    },
    fixable: null,
    schema: [],
    messages: {
      deprecated: "'.native' modifier on 'v-on' directive is deprecated."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VIdentifier & {parent:VDirectiveKey} } node */
      "VAttribute[directive=true][key.name.name='on'] > VDirectiveKey > VIdentifier[name='native']"(
        node
      ) {
        const key = node.parent
        if (!key.modifiers.includes(node)) return

        context.report({
          node,
          messageId: 'deprecated'
        })
      }
    })
  }
}


code:
/**
 * @fileoverview disallow using deprecated number (keycode) modifiers
 * @author yoyo930021
 */
'use strict'

const utils = require('../utils')
const keyCodeToKey = require('../utils/keycode-to-key')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-on-number-modifiers.html'
    },
    fixable: 'code',
    schema: [],
    messages: {
      numberModifierIsDeprecated:
        "'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VDirectiveKey} node */
      "VAttribute[directive=true][key.name.name='on'] > VDirectiveKey"(node) {
        const modifier = node.modifiers.find((mod) =>
          Number.isInteger(Number.parseInt(mod.name, 10))
        )
        if (!modifier) return

        const keyCodes = Number.parseInt(modifier.name, 10)
        if (keyCodes > 9 || keyCodes < 0) {
          context.report({
            node: modifier,
            messageId: 'numberModifierIsDeprecated',
            fix(fixer) {
              const key = keyCodeToKey[keyCodes]
              if (!key) return null

              return fixer.replaceText(modifier, `${key}`)
            }
          })
        }
      }
    })
  }
}


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

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-deprecated-vue-config-keycodes.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: '`Vue.config.keyCodes` are deprecated.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return {
      /** @param {MemberExpression} node */
      "MemberExpression[property.type='Identifier'][property.name='keyCodes']"(
        node
      ) {
        const config = utils.skipChainExpression(node.object)
        if (
          config.type !== 'MemberExpression' ||
          config.property.type !== 'Identifier' ||
          config.property.name !== 'config' ||
          config.object.type !== 'Identifier' ||
          config.object.name !== 'Vue'
        ) {
          return
        }
        context.report({
          node,
          messageId: 'unexpected'
        })
      }
    }
  }
}


code:
/**
 * @fileoverview Prevents duplication of field names.
 * @author Armano
 */
'use strict'

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

/**
 * @typedef {import('../utils').GroupName} GroupName
 * @typedef {import('eslint').Scope.Variable} Variable
 * @typedef {import('../utils').ComponentProp} ComponentProp
 */

/** @type {GroupName[]} */
const GROUP_NAMES = ['props', 'computed', 'data', 'methods', 'setup']

/**
 * Gets the props pattern node from given `defineProps()` node
 * @param {CallExpression} node
 * @returns {Pattern|null}
 */
function getPropsPattern(node) {
  let target = node
  if (
    target.parent &&
    target.parent.type === 'CallExpression' &&
    target.parent.arguments[0] === target &&
    target.parent.callee.type === 'Identifier' &&
    target.parent.callee.name === 'withDefaults'
  ) {
    target = target.parent
  }

  if (
    !target.parent ||
    target.parent.type !== 'VariableDeclarator' ||
    target.parent.init !== target
  ) {
    return null
  }
  return target.parent.id
}

/**
 * Checks whether the initialization of the given variable declarator node contains one of the references.
 * @param {VariableDeclarator} node
 * @param {ESNode[]} references
 */
function isInsideInitializer(node, references) {
  const init = node.init
  if (!init) {
    return false
  }
  return references.some(
    (id) => init.range[0] <= id.range[0] && id.range[1] <= init.range[1]
  )
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow duplication of field names',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-dupe-keys.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          groups: {
            type: 'array'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      duplicateKey:
        "Duplicate key '{{name}}'. May cause name collision in script or template tag."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    const groups = new Set([...GROUP_NAMES, ...(options.groups || [])])

    return utils.compositingVisitors(
      utils.executeOnVue(context, (obj) => {
        const properties = utils.iterateProperties(obj, groups)
        /** @type {Set<string>} */
        const usedNames = new Set()
        for (const o of properties) {
          if (usedNames.has(o.name)) {
            context.report({
              node: o.node,
              messageId: 'duplicateKey',
              data: {
                name: o.name
              }
            })
          }

          usedNames.add(o.name)
        }
      }),
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(node, props) {
          const propsNode = getPropsPattern(node)
          const propReferences = [
            ...(propsNode ? extractReferences(propsNode) : []),
            node
          ]

          for (const prop of props) {
            if (!prop.propName) continue

            const variable = findVariable(
              utils.getScope(context, node),
              prop.propName
            )
            if (!variable || variable.defs.length === 0) continue

            if (
              variable.defs.some((def) => {
                if (def.type !== 'Variable') return false
                return isInsideInitializer(def.node, propReferences)
              })
            ) {
              continue
            }

            context.report({
              node: variable.defs[0].node,
              messageId: 'duplicateKey',
              data: {
                name: prop.propName
              }
            })
          }
        }
      })
    )

    /**
     * Extracts references from the given node.
     * @param {Pattern} node
     * @returns {Identifier[]} References
     */
    function extractReferences(node) {
      if (node.type === 'Identifier') {
        const variable = findVariable(utils.getScope(context, node), node)
        if (!variable) {
          return []
        }
        return variable.references.map((ref) => ref.identifier)
      }
      if (node.type === 'ObjectPattern') {
        return node.properties.flatMap((prop) =>
          extractReferences(prop.type === 'Property' ? prop.value : prop)
        )
      }
      if (node.type === 'AssignmentPattern') {
        return extractReferences(node.left)
      }
      if (node.type === 'RestElement') {
        return extractReferences(node.argument)
      }
      return []
    }
  }
}


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

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

/**
 * @typedef {NonNullable<VExpressionContainer['expression']>} VExpression
 */
/**
 * @typedef {object} OrOperands
 * @property {VExpression} OrOperands.node
 * @property {AndOperands[]} OrOperands.operands
 *
 * @typedef {object} AndOperands
 * @property {VExpression} AndOperands.node
 * @property {VExpression[]} AndOperands.operands
 */
/**
 * Splits the given node by the given logical operator.
 * @param {string} operator Logical operator `||` or `&&`.
 * @param {VExpression} node The node to split.
 * @returns {VExpression[]} Array of conditions that makes the node when joined by the operator.
 */
function splitByLogicalOperator(operator, node) {
  if (node.type === 'LogicalExpression' && node.operator === operator) {
    return [
      ...splitByLogicalOperator(operator, node.left),
      ...splitByLogicalOperator(operator, node.right)
    ]
  }
  return [node]
}

/**
 * @param {VExpression} node
 */
function splitByOr(node) {
  return splitByLogicalOperator('||', node)
}
/**
 * @param {VExpression} node
 */
function splitByAnd(node) {
  return splitByLogicalOperator('&&', node)
}

/**
 * @param {VExpression} node
 * @returns {OrOperands}
 */
function buildOrOperands(node) {
  const orOperands = splitByOr(node)
  return {
    node,
    operands: orOperands.map((orOperand) => {
      const andOperands = splitByAnd(orOperand)
      return {
        node: orOperand,
        operands: andOperands
      }
    })
  }
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow duplicate conditions in `v-if` / `v-else-if` chains',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-dupe-v-else-if.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected:
        'This branch can never execute. Its condition is a duplicate or covered by previous conditions in the `v-if` / `v-else-if` chain.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const sourceCode = context.getSourceCode()
    const tokenStore =
      sourceCode.parserServices.getTemplateBodyTokenStore &&
      sourceCode.parserServices.getTemplateBodyTokenStore()
    /**
     * Determines whether the two given nodes are considered to be equal. In particular, given that the nodes
     * represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators.
     * @param {VExpression} a First node.
     * @param {VExpression} 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
      }

      if (
        a.type === 'LogicalExpression' &&
        b.type === 'LogicalExpression' &&
        (a.operator === '||' || a.operator === '&&') &&
        a.operator === b.operator
      ) {
        return (
          (equal(a.left, b.left) && equal(a.right, b.right)) ||
          (equal(a.left, b.right) && equal(a.right, b.left))
        )
      }

      return utils.equalTokens(a, b, tokenStore)
    }

    /**
     * Determines whether the first given AndOperands is a subset of the second given AndOperands.
     *
     * e.g. A: (a && b), B: (a && b && c): B is a subset of A.
     *
     * @param {AndOperands} operandsA The AndOperands to compare from.
     * @param {AndOperands} operandsB The AndOperands to compare against.
     * @returns {boolean} `true` if the `andOperandsA` is a subset of the `andOperandsB`.
     */
    function isSubset(operandsA, operandsB) {
      return operandsA.operands.every((operandA) =>
        operandsB.operands.some((operandB) => equal(operandA, operandB))
      )
    }

    return utils.defineTemplateBodyVisitor(context, {
      "VAttribute[directive=true][key.name.name='else-if']"(node) {
        if (!node.value || !node.value.expression) {
          return
        }
        const test = node.value.expression
        const conditionsToCheck =
          test.type === 'LogicalExpression' && test.operator === '&&'
            ? [...splitByAnd(test), test]
            : [test]
        const listToCheck = conditionsToCheck.map(buildOrOperands)

        /** @type {VElement | null} */
        let current = node.parent.parent
        while (current && (current = utils.prevSibling(current))) {
          const vIf = utils.getDirective(current, 'if')
          const currentTestDir = vIf || utils.getDirective(current, 'else-if')
          if (!currentTestDir) {
            return
          }
          if (currentTestDir.value && currentTestDir.value.expression) {
            const currentOrOperands = buildOrOperands(
              currentTestDir.value.expression
            )

            for (const condition of listToCheck) {
              const operands = (condition.operands = condition.operands.filter(
                (orOperand) =>
                  !currentOrOperands.operands.some((currentOrOperand) =>
                    isSubset(currentOrOperand, orOperand)
                  )
              ))
              if (operands.length === 0) {
                context.report({
                  node: condition.node,
                  messageId: 'unexpected'
                })
                return
              }
            }
          }

          if (vIf) {
            return
          }
        }
      }
    })
  }
}


code:
/**
 * @fileoverview Disable inheritAttrs when using v-bind="$attrs"
 * @author Hiroki Osame
 */
'use strict'

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

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"`',
      categories: undefined,
      recommended: false,
      url: 'https://eslint.vuejs.org/rules/no-duplicate-attr-inheritance.html'
    },
    fixable: null,
    schema: [],
    messages: {
      noDuplicateAttrInheritance: 'Set "inheritAttrs" to false.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {string | number | boolean | RegExp | BigInt | null} */
    let inheritsAttrs = true

    /** @param {ObjectExpression} node */
    function processOptions(node) {
      const inheritAttrsProp = utils.findProperty(node, 'inheritAttrs')

      if (inheritAttrsProp && inheritAttrsProp.value.type === 'Literal') {
        inheritsAttrs = inheritAttrsProp.value.value
      }
    }

    return utils.compositingVisitors(
      utils.executeOnVue(context, processOptions),
      utils.defineScriptSetupVisitor(context, {
        onDefineOptionsEnter(node) {
          if (node.arguments.length === 0) return
          const define = node.arguments[0]
          if (define.type !== 'ObjectExpression') return
          processOptions(define)
        }
      }),
      utils.defineTemplateBodyVisitor(context, {
        /** @param {VExpressionContainer} node */
        "VAttribute[directive=true][key.name.name='bind'][key.argument=null] > VExpressionContainer"(
          node
        ) {
          if (!inheritsAttrs) {
            return
          }
          const attrsRef = node.references.find((reference) => {
            if (reference.variable != null) {
              // Not vm reference
              return false
            }
            return reference.id.name === '$attrs'
          })

          if (attrsRef) {
            context.report({
              node: attrsRef.id,
              messageId: 'noDuplicateAttrInheritance'
            })
          }
        }
      })
    )
  }
}


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')

/**
 * Get the name of the given attribute node.
 * @param {VAttribute | VDirective} attribute The attribute node to get.
 * @returns {string | null} The name of the attribute.
 */
function getName(attribute) {
  if (!attribute.directive) {
    return attribute.key.name
  }
  if (attribute.key.name.name === 'bind') {
    return (
      (attribute.key.argument &&
        attribute.key.argument.type === 'VIdentifier' &&
        attribute.key.argument.name) ||
      null
    )
  }
  return null
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow duplication of attributes',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-duplicate-attributes.html'
    },
    fixable: null,

    schema: [
      {
        type: 'object',
        properties: {
          allowCoexistClass: {
            type: 'boolean'
          },
          allowCoexistStyle: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      duplicateAttribute: "Duplicate attribute '{{name}}'."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    const allowCoexistStyle = options.allowCoexistStyle !== false
    const allowCoexistClass = options.allowCoexistClass !== false

    /** @type {Set<string>} */
    const directiveNames = new Set()
    /** @type {Set<string>} */
    const attributeNames = new Set()

    /**
     * @param {string} name
     * @param {boolean} isDirective
     */
    function isDuplicate(name, isDirective) {
      if (
        (allowCoexistStyle && name === 'style') ||
        (allowCoexistClass && name === 'class')
      ) {
        return isDirective ? directiveNames.has(name) : attributeNames.has(name)
      }
      return directiveNames.has(name) || attributeNames.has(name)
    }

    return utils.defineTemplateBodyVisitor(context, {
      VStartTag() {
        directiveNames.clear()
        attributeNames.clear()
      },
      VAttribute(node) {
        const name = getName(node)
        if (name == null) {
          return
        }

        if (isDuplicate(name, node.directive)) {
          context.report({
            node,
            loc: node.loc,
            messageId: 'duplicateAttribute',
            data: { name }
          })
        }

        if (node.directive) {
          directiveNames.add(name)
        } else {
          attributeNames.add(name)
        }
      }
    })
  }
}


code:
/**
 * @author tyankatsu <https://github.com/tyankatsu0105>
 * See LICENSE file in root directory for full license.
 */
'use strict'

const { isVElement } = require('../utils')

/**
 * check whether has attribute `src`
 * @param {VElement} componentBlock
 */
function hasAttributeSrc(componentBlock) {
  const hasAttribute = componentBlock.startTag.attributes.length > 0

  const hasSrc = componentBlock.startTag.attributes.some(
    (attribute) =>
      !attribute.directive &&
      attribute.key.name === 'src' &&
      attribute.value &&
      attribute.value.value !== ''
  )

  return hasAttribute && hasSrc
}

/**
 * check whether value under the component block is only whitespaces or break lines
 * @param {VElement} componentBlock
 */
function isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) {
  return (
    componentBlock.children.length === 1 &&
    componentBlock.children[0].type === 'VText' &&
    !componentBlock.children[0].value.trim()
  )
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'disallow the `<template>` `<script>` `<style>` block to be empty',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-empty-component-block.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: '`<{{ blockName }}>` is empty. Empty block is not allowed.'
    }
  },

  /**
   * @param {RuleContext} context - The rule context.
   * @returns {RuleListener} AST event handlers.
   */
  create(context) {
    const sourceCode = context.getSourceCode()
    if (!sourceCode.parserServices.getDocumentFragment) {
      return {}
    }
    const documentFragment = sourceCode.parserServices.getDocumentFragment()
    if (!documentFragment) {
      return {}
    }

    const componentBlocks = documentFragment.children.filter(isVElement)

    return {
      Program() {
        for (const componentBlock of componentBlocks) {
          if (
            componentBlock.name !== 'template' &&
            componentBlock.name !== 'script' &&
            componentBlock.name !== 'style'
          )
            continue

          // https://vue-loader.vuejs.org/spec.html#src-imports
          if (hasAttributeSrc(componentBlock)) continue

          if (
            isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) ||
            componentBlock.children.length === 0
          ) {
            context.report({
              node: componentBlock,
              loc: componentBlock.loc,
              messageId: 'unexpected',
              data: {
                blockName: componentBlock.name
              }
            })
          }
        }
      }
    }
  }
}


code:
/**
 * @author Yosuke Ota
 */
'use strict'

const { wrapCoreRule } = require('../utils')

// eslint-disable-next-line internal/no-invalid-meta
module.exports = wrapCoreRule('no-empty-pattern')


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

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

/**
 * @typedef {import('@typescript-eslint/types').TSESTree.ExportAllDeclaration} TSESTreeExportAllDeclaration
 * @typedef {import('@typescript-eslint/types').TSESTree.ExportDefaultDeclaration} TSESTreeExportDefaultDeclaration
 * @typedef {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration} TSESTreeExportNamedDeclaration
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow `export` in `<script setup>`',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-export-in-script-setup.html'
    },
    fixable: null,
    schema: [],
    messages: {
      forbidden: '`<script setup>` cannot contain ES module exports.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @param {ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration} node */
    function verify(node) {
      const tsNode =
        /** @type {TSESTreeExportAllDeclaration | TSESTreeExportDefaultDeclaration | TSESTreeExportNamedDeclaration} */ (
          node
        )
      if (tsNode.exportKind === 'type') {
        return
      }
      if (
        tsNode.type === 'ExportNamedDeclaration' &&
        tsNode.specifiers.length > 0 &&
        tsNode.specifiers.every((spec) => spec.exportKind === 'type')
      ) {
        return
      }
      context.report({
        node,
        messageId: 'forbidden'
      })
    }

    return utils.defineScriptSetupVisitor(context, {
      ExportAllDeclaration: verify,
      ExportDefaultDeclaration: verify,
      ExportNamedDeclaration: verify
    })
  }
}


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

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

/**
 * Get the callee member node from the given CallExpression
 * @param {CallExpression} node CallExpression
 */
function getCalleeMemberNode(node) {
  const callee = utils.skipChainExpression(node.callee)

  if (callee.type === 'MemberExpression') {
    const name = utils.getStaticPropertyName(callee)
    if (name) {
      return { name, member: callee }
    }
  }
  return null
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow asynchronously registered `expose`',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-expose-after-await.html'
    },
    fixable: null,
    schema: [],
    messages: {
      forbidden: '`{{name}}` is forbidden after an `await` expression.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @typedef {object} SetupScopeData
     * @property {boolean} afterAwait
     * @property {[number,number]} range
     * @property {(node: Identifier, callNode: CallExpression) => boolean} isExposeReferenceId
     * @property {(node: Identifier) => boolean} isContextReferenceId
     */
    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program} scopeNode
     */
    /** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program, SetupScopeData>} */
    const setupScopes = new Map()

    /** @type {ScopeStack | null} */
    let scopeStack = null

    return utils.compositingVisitors(
      {
        /**
         * @param {Program} node
         */
        Program(node) {
          scopeStack = {
            upper: scopeStack,
            scopeNode: node
          }
        }
      },
      {
        /**
         * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
         */
        ':function'(node) {
          scopeStack = {
            upper: scopeStack,
            scopeNode: node
          }
        },
        ':function:exit'() {
          scopeStack = scopeStack && scopeStack.upper
        },
        /** @param {AwaitExpression} node */
        AwaitExpression(node) {
          if (!scopeStack) {
            return
          }
          const setupScope = setupScopes.get(scopeStack.scopeNode)
          if (!setupScope || !utils.inRange(setupScope.range, node)) {
            return
          }
          setupScope.afterAwait = true
        },
        /** @param {CallExpression} node */
        CallExpression(node) {
          if (!scopeStack) {
            return
          }
          const setupScope = setupScopes.get(scopeStack.scopeNode)
          if (
            !setupScope ||
            !setupScope.afterAwait ||
            !utils.inRange(setupScope.range, node)
          ) {
            return
          }
          const { isContextReferenceId, isExposeReferenceId } = setupScope
          if (
            node.callee.type === 'Identifier' &&
            isExposeReferenceId(node.callee, node)
          ) {
            // setup(props,{expose}) {expose()}
            context.report({
              node,
              messageId: 'forbidden',
              data: {
                name: node.callee.name
              }
            })
          } else {
            const expose = getCalleeMemberNode(node)
            if (
              expose &&
              expose.name === 'expose' &&
              expose.member.object.type === 'Identifier' &&
              isContextReferenceId(expose.member.object)
            ) {
              // setup(props,context) {context.emit()}
              context.report({
                node,
                messageId: 'forbidden',
                data: {
                  name: expose.name
                }
              })
            }
          }
        }
      },
      (() => {
        const scriptSetup = utils.getScriptSetupElement(context)
        if (!scriptSetup) {
          return {}
        }
        return {
          /**
           * @param {Program} node
           */
          Program(node) {
            setupScopes.set(node, {
              afterAwait: false,
              range: scriptSetup.range,
              isExposeReferenceId: (id, callNode) =>
                callNode.parent.type === 'ExpressionStatement' &&
                callNode.parent.parent === node &&
                id.name === 'defineExpose',
              isContextReferenceId: () => false
            })
          }
        }
      })(),
      utils.defineVueVisitor(context, {
        onSetupFunctionEnter(node) {
          const contextParam = node.params[1]
          if (!contextParam) {
            // no arguments
            return
          }
          if (contextParam.type === 'RestElement') {
            // cannot check
            return
          }
          if (contextParam.type === 'ArrayPattern') {
            // cannot check
            return
          }
          /** @type {Set<Identifier>} */
          const contextReferenceIds = new Set()
          /** @type {Set<Identifier>} */
          const exposeReferenceIds = new Set()
          if (contextParam.type === 'ObjectPattern') {
            const exposeProperty = utils.findAssignmentProperty(
              contextParam,
              'expose'
            )
            if (!exposeProperty) {
              return
            }
            const exposeParam = exposeProperty.value
            // `setup(props, {emit})`
            const variable =
              exposeParam.type === 'Identifier'
                ? findVariable(
                    utils.getScope(context, exposeParam),
                    exposeParam
                  )
                : null
            if (!variable) {
              return
            }
            for (const reference of variable.references) {
              if (!reference.isRead()) {
                continue
              }
              exposeReferenceIds.add(reference.identifier)
            }
          } else if (contextParam.type === 'Identifier') {
            // `setup(props, context)`
            const variable = findVariable(
              utils.getScope(context, contextParam),
              contextParam
            )
            if (!variable) {
              return
            }
            for (const reference of variable.references) {
              if (!reference.isRead()) {
                continue
              }
              contextReferenceIds.add(reference.identifier)
            }
          }
          setupScopes.set(node, {
            afterAwait: false,
            range: node.range,
            isExposeReferenceId: (id) => exposeReferenceIds.has(id),
            isContextReferenceId: (id) => contextReferenceIds.has(id)
          })
        },
        onSetupFunctionExit(node) {
          setupScopes.delete(node)
        }
      })
    )
  }
}