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

const { isParenthesized } = require('@eslint-community/eslint-utils')
const { wrapStylisticOrCoreRule } = require('../utils')
const { getStyleVariablesContext } = require('../utils/style-variables')

// eslint-disable-next-line internal/no-invalid-meta
module.exports = wrapStylisticOrCoreRule('no-extra-parens', {
  skipDynamicArguments: true,
  applyDocument: true,
  create: createForVueSyntax
})

/**
 * Check whether the given token is a left parenthesis.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a left parenthesis.
 */
function isLeftParen(token) {
  return token.type === 'Punctuator' && token.value === '('
}

/**
 * Check whether the given token is a right parenthesis.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a right parenthesis.
 */
function isRightParen(token) {
  return token.type === 'Punctuator' && token.value === ')'
}

/**
 * Check whether the given token is a left brace.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a left brace.
 */
function isLeftBrace(token) {
  return token.type === 'Punctuator' && token.value === '{'
}

/**
 * Check whether the given token is a right brace.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a right brace.
 */
function isRightBrace(token) {
  return token.type === 'Punctuator' && token.value === '}'
}

/**
 * Check whether the given token is a left bracket.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a left bracket.
 */
function isLeftBracket(token) {
  return token.type === 'Punctuator' && token.value === '['
}

/**
 * Check whether the given token is a right bracket.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a right bracket.
 */
function isRightBracket(token) {
  return token.type === 'Punctuator' && token.value === ']'
}

/**
 * Determines if a given expression node is an IIFE
 * @param {Expression} node The node to check
 * @returns {node is CallExpression & { callee: FunctionExpression } } `true` if the given node is an IIFE
 */
function isIIFE(node) {
  return (
    node.type === 'CallExpression' && node.callee.type === 'FunctionExpression'
  )
}

/**
 * @param {RuleContext} context - The rule context.
 * @returns {TemplateListener} AST event handlers.
 */
function createForVueSyntax(context) {
  const sourceCode = context.getSourceCode()
  if (!sourceCode.parserServices.getTemplateBodyTokenStore) {
    return {}
  }
  const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()

  /**
   * Checks if the given node turns into a filter when unwraped.
   * @param {Expression} expression node to evaluate
   * @returns {boolean} `true` if the given node turns into a filter when unwraped.
   */
  function isUnwrapChangeToFilter(expression) {
    let parenStack = null
    for (const token of tokenStore.getTokens(expression)) {
      if (parenStack) {
        if (parenStack.isUpToken(token)) {
          parenStack = parenStack.upper
          continue
        }
      } else {
        if (token.value === '|') {
          return true
        }
      }
      if (isLeftParen(token)) {
        parenStack = { isUpToken: isRightParen, upper: parenStack }
      } else if (isLeftBracket(token)) {
        parenStack = { isUpToken: isRightBracket, upper: parenStack }
      } else if (isLeftBrace(token)) {
        parenStack = { isUpToken: isRightBrace, upper: parenStack }
      }
    }
    return false
  }
  /**
   * Checks if the given node is CSS v-bind() without quote.
   * @param {VExpressionContainer} node
   * @param {Expression} expression
   */
  function isStyleVariableWithoutQuote(node, expression) {
    const styleVars = getStyleVariablesContext(context)
    if (!styleVars || !styleVars.vBinds.includes(node)) {
      return false
    }

    const vBindToken = tokenStore.getFirstToken(node)
    const tokens = tokenStore.getTokensBetween(vBindToken, expression)

    return tokens.every(isLeftParen)
  }
  /**
   * @param {VExpressionContainer & { expression: Expression | VFilterSequenceExpression | null }} node
   */
  function verify(node) {
    if (!node.expression) {
      return
    }

    const expression =
      node.expression.type === 'VFilterSequenceExpression'
        ? node.expression.expression
        : node.expression

    if (!isParenthesized(expression, tokenStore)) {
      return
    }

    if (!isParenthesized(2, expression, tokenStore)) {
      if (
        isIIFE(expression) &&
        !isParenthesized(expression.callee, tokenStore)
      ) {
        return
      }
      if (isUnwrapChangeToFilter(expression)) {
        return
      }
      if (isStyleVariableWithoutQuote(node, expression)) {
        return
      }
    }
    report(expression)
  }

  /**
   * Report the node
   * @param {Expression} node node to evaluate
   * @returns {void}
   * @private
   */
  function report(node) {
    const sourceCode = context.getSourceCode()
    const leftParenToken = tokenStore.getTokenBefore(node)
    const rightParenToken = tokenStore.getTokenAfter(node)

    context.report({
      node,
      loc: leftParenToken.loc,
      messageId: 'unexpected',
      fix(fixer) {
        const parenthesizedSource = sourceCode.text.slice(
          leftParenToken.range[1],
          rightParenToken.range[0]
        )

        return fixer.replaceTextRange(
          [leftParenToken.range[0], rightParenToken.range[1]],
          parenthesizedSource
        )
      }
    })
  }

  return {
    "VAttribute[directive=true][key.name.name='bind'] > VExpressionContainer":
      verify,
    'VElement > VExpressionContainer': verify
  }
}


code:
'use strict'

const baseRule = require('./valid-model-definition')

module.exports = {
  // eslint-disable-next-line eslint-plugin/prefer-message-ids
  meta: {
    ...baseRule.meta,
    // eslint-disable-next-line eslint-plugin/meta-property-ordering
    type: baseRule.meta.type,
    docs: {
      description: baseRule.meta.docs.description,
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-invalid-model-keys.html'
    },
    deprecated: true,
    replacedBy: ['valid-model-definition'],
    schema: []
  },
  /** @param {RuleContext} context */
  create(context) {
    return baseRule.create(context)
  }
}

license: mit


code:
/**
 * @author Yosuke Ota
 * @fileoverview Rule to disalow whitespace that is not a tab or space, whitespace inside strings and comments are allowed
 */

'use strict'

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

const ALL_IRREGULARS =
  /[\f\v\u0085\uFEFF\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000\u2028\u2029]/u
const IRREGULAR_WHITESPACE =
  /[\f\v\u0085\uFEFF\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000]+/gmu
const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/gmu

module.exports = {
  meta: {
    type: 'problem',

    docs: {
      description: 'disallow irregular whitespace in `.vue` files',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-irregular-whitespace.html',
      extensionSource: {
        url: 'https://eslint.org/docs/rules/no-irregular-whitespace',
        name: 'ESLint core'
      }
    },

    schema: [
      {
        type: 'object',
        properties: {
          skipComments: {
            type: 'boolean',
            default: false
          },
          skipStrings: {
            type: 'boolean',
            default: true
          },
          skipTemplates: {
            type: 'boolean',
            default: false
          },
          skipRegExps: {
            type: 'boolean',
            default: false
          },
          skipHTMLAttributeValues: {
            type: 'boolean',
            default: false
          },
          skipHTMLTextContents: {
            type: 'boolean',
            default: false
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      disallow: 'Irregular whitespace not allowed.'
    }
  },
  /**
   * @param {RuleContext} context - The rule context.
   * @returns {RuleListener} AST event handlers.
   */
  create(context) {
    // Module store of error indexes that we have found
    /** @type {number[]} */
    let errorIndexes = []

    // Lookup the `skipComments` option, which defaults to `false`.
    const options = context.options[0] || {}
    const skipComments = !!options.skipComments
    const skipStrings = options.skipStrings !== false
    const skipRegExps = !!options.skipRegExps
    const skipTemplates = !!options.skipTemplates
    const skipHTMLAttributeValues = !!options.skipHTMLAttributeValues
    const skipHTMLTextContents = !!options.skipHTMLTextContents

    const sourceCode = context.getSourceCode()

    /**
     * Removes errors that occur inside a string node
     * @param {ASTNode | Token} node to check for matching errors.
     * @returns {void}
     * @private
     */
    function removeWhitespaceError(node) {
      const [startIndex, endIndex] = node.range

      errorIndexes = errorIndexes.filter(
        (errorIndex) => errorIndex < startIndex || endIndex <= errorIndex
      )
    }

    /**
     * Checks literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
     * @param {Literal} node to check for matching errors.
     * @returns {void}
     * @private
     */
    function removeInvalidNodeErrorsInLiteral(node) {
      const shouldCheckStrings = skipStrings && typeof node.value === 'string'
      const shouldCheckRegExps = skipRegExps && Boolean(node.regex)

      // If we have irregular characters, remove them from the errors list
      if (
        (shouldCheckStrings || shouldCheckRegExps) &&
        ALL_IRREGULARS.test(sourceCode.getText(node))
      ) {
        removeWhitespaceError(node)
      }
    }

    /**
     * Checks template string literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
     * @param {TemplateElement} node to check for matching errors.
     * @returns {void}
     * @private
     */
    function removeInvalidNodeErrorsInTemplateLiteral(node) {
      if (ALL_IRREGULARS.test(node.value.raw)) {
        removeWhitespaceError(node)
      }
    }

    /**
     * Checks HTML attribute value nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
     * @param {VLiteral} node to check for matching errors.
     * @returns {void}
     * @private
     */
    function removeInvalidNodeErrorsInHTMLAttributeValue(node) {
      if (ALL_IRREGULARS.test(sourceCode.getText(node))) {
        removeWhitespaceError(node)
      }
    }

    /**
     * Checks HTML text content nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
     * @param {VText} node to check for matching errors.
     * @returns {void}
     * @private
     */
    function removeInvalidNodeErrorsInHTMLTextContent(node) {
      if (ALL_IRREGULARS.test(sourceCode.getText(node))) {
        removeWhitespaceError(node)
      }
    }

    /**
     * Checks comment nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
     * @param {Comment | HTMLComment | HTMLBogusComment} node to check for matching errors.
     * @returns {void}
     * @private
     */
    function removeInvalidNodeErrorsInComment(node) {
      if (ALL_IRREGULARS.test(node.value)) {
        removeWhitespaceError(node)
      }
    }

    /**
     * Checks the program source for irregular whitespaces and irregular line terminators
     * @returns {void}
     * @private
     */
    function checkForIrregularWhitespace() {
      const source = sourceCode.getText()
      let match
      while ((match = IRREGULAR_WHITESPACE.exec(source)) !== null) {
        errorIndexes.push(match.index)
      }
      while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) {
        errorIndexes.push(match.index)
      }
    }

    checkForIrregularWhitespace()

    if (errorIndexes.length === 0) {
      return {}
    }
    const bodyVisitor = utils.defineTemplateBodyVisitor(context, {
      ...(skipHTMLAttributeValues
        ? {
            'VAttribute[directive=false] > VLiteral':
              removeInvalidNodeErrorsInHTMLAttributeValue
          }
        : {}),
      ...(skipHTMLTextContents
        ? { VText: removeInvalidNodeErrorsInHTMLTextContent }
        : {}),

      // inline scripts
      Literal: removeInvalidNodeErrorsInLiteral,
      ...(skipTemplates
        ? { TemplateElement: removeInvalidNodeErrorsInTemplateLiteral }
        : {})
    })
    return {
      ...bodyVisitor,
      Literal: removeInvalidNodeErrorsInLiteral,
      ...(skipTemplates
        ? { TemplateElement: removeInvalidNodeErrorsInTemplateLiteral }
        : {}),
      'Program:exit'(node) {
        if (bodyVisitor['Program:exit']) {
          bodyVisitor['Program:exit'](node)
        }
        const templateBody = node.templateBody
        if (skipComments) {
          // First strip errors occurring in comment nodes.
          for (const node of sourceCode.getAllComments()) {
            removeInvalidNodeErrorsInComment(node)
          }
          if (templateBody) {
            for (const node of templateBody.comments) {
              removeInvalidNodeErrorsInComment(node)
            }
          }
        }

        // Removes errors that occur outside script and template
        const [scriptStart, scriptEnd] = node.range
        const [templateStart, templateEnd] = templateBody
          ? templateBody.range
          : [0, 0]
        errorIndexes = errorIndexes.filter(
          (errorIndex) =>
            (scriptStart <= errorIndex && errorIndex < scriptEnd) ||
            (templateStart <= errorIndex && errorIndex < templateEnd)
        )

        // If we have any errors remaining, report on them
        for (const errorIndex of errorIndexes) {
          context.report({
            loc: sourceCode.getLocFromIndex(errorIndex),
            messageId: 'disallow'
          })
        }
      }
    }
  }
}


code:
/**
 * @author Yosuke Ota
 * See LICENSE file in root directory for full license.
 */
'use strict'
const { ReferenceTracker } = require('@eslint-community/eslint-utils')
const utils = require('../utils')

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

const LIFECYCLE_HOOKS = [
  'onBeforeMount',
  'onBeforeUnmount',
  'onBeforeUpdate',
  'onErrorCaptured',
  'onMounted',
  'onRenderTracked',
  'onRenderTriggered',
  'onUnmounted',
  'onUpdated',
  'onActivated',
  'onDeactivated'
]

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow asynchronously registered lifecycle hooks',
      categories: ['vue3-essential'],
      url: 'https://eslint.vuejs.org/rules/no-lifecycle-after-await.html'
    },
    fixable: null,
    schema: [],
    messages: {
      forbidden: 'Lifecycle hooks are forbidden after an `await` expression.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @typedef {object} SetupScopeData
     * @property {boolean} afterAwait
     * @property {[number,number]} range
     */
    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} scopeNode
     */
    /** @type {Set<ESNode>} */
    const lifecycleHookCallNodes = new Set()
    /** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, SetupScopeData>} */
    const setupScopes = new Map()

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

    return utils.compositingVisitors(
      {
        /** @param {Program} program */
        Program(program) {
          const tracker = new ReferenceTracker(utils.getScope(context, program))
          /** @type {TraceMap} */
          const traceMap = {}
          for (const lifecycleHook of LIFECYCLE_HOOKS) {
            traceMap[lifecycleHook] = {
              [ReferenceTracker.CALL]: true
            }
          }

          for (const { node } of utils.iterateReferencesTraceMap(
            tracker,
            traceMap
          )) {
            lifecycleHookCallNodes.add(node)
          }
        }
      },
      utils.defineVueVisitor(context, {
        onSetupFunctionEnter(node) {
          setupScopes.set(node, {
            afterAwait: false,
            range: node.range
          })
        },
        /**
         * @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
          }

          if (lifecycleHookCallNodes.has(node)) {
            if (node.arguments.length >= 2) {
              // Has target instance. e.g. `onMounted(() => {}, instance)`
              return
            }
            context.report({
              node,
              messageId: 'forbidden'
            })
          }
        },
        onSetupFunctionExit(node) {
          setupScopes.delete(node)
        }
      })
    )
  }
}


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

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

// https://github.com/vuejs/vue-next/blob/64e2f4643602c5980361e66674141e61ba60ef70/packages/compiler-core/src/parse.ts#L405
const SPECIAL_TEMPLATE_DIRECTIVES = new Set([
  'if',
  'else',
  'else-if',
  'for',
  'slot'
])

/**
 * @param {VAttribute | VDirective} attr
 */
function getKeyName(attr) {
  if (attr.directive) {
    if (attr.key.name.name !== 'bind') {
      // no v-bind
      return null
    }
    if (
      !attr.key.argument ||
      attr.key.argument.type === 'VExpressionContainer'
    ) {
      // unknown
      return null
    }
    return attr.key.argument.name
  }
  return attr.key.name
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow unnecessary `<template>`',
      categories: ['vue3-recommended', 'vue2-recommended'],
      url: 'https://eslint.vuejs.org/rules/no-lone-template.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          ignoreAccessible: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      requireDirective: '`<template>` require directive.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    const ignoreAccessible = options.ignoreAccessible === true

    return utils.defineTemplateBodyVisitor(context, {
      /** @param {VStartTag} node */
      "VElement[name='template'][parent.type='VElement'] > VStartTag"(node) {
        if (
          node.attributes.some((attr) => {
            if (attr.directive) {
              const directiveName = attr.key.name.name
              if (SPECIAL_TEMPLATE_DIRECTIVES.has(directiveName)) {
                return true
              }
              if (directiveName === 'slot-scope') {
                // `slot-scope` is deprecated in Vue.js 2.6
                return true
              }
              if (directiveName === 'scope') {
                // `scope` is deprecated in Vue.js 2.5
                return true
              }
            }

            const keyName = getKeyName(attr)
            if (keyName === 'slot') {
              // `slot` is deprecated in Vue.js 2.6
              return true
            }

            return false
          })
        ) {
          return
        }

        if (
          ignoreAccessible &&
          node.attributes.some((attr) => {
            const keyName = getKeyName(attr)
            return keyName === 'id' || keyName === 'ref'
          })
        ) {
          return
        }

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


code:
/**
 * @author Yosuke Ota
 * 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-loss-of-precision', {
  applyDocument: true
})


code:
/**
 * @fileoverview This rule warns about the usage of extra whitespaces between attributes
 * @author Armano
 */
'use strict'

const path = require('path')

/**
 * @param {RuleContext} context
 * @param {Token} node
 */
const isProperty = (context, node) => {
  const sourceCode = context.getSourceCode()
  return node.type === 'Punctuator' && sourceCode.getText(node) === ':'
}

module.exports = {
  meta: {
    type: 'layout',
    docs: {
      description: 'disallow multiple spaces',
      categories: ['vue3-strongly-recommended', 'vue2-strongly-recommended'],
      url: 'https://eslint.vuejs.org/rules/no-multi-spaces.html'
    },
    fixable: 'whitespace',
    schema: [
      {
        type: 'object',
        properties: {
          ignoreProperties: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      multipleSpaces: "Multiple spaces found before '{{displayValue}}'.",
      useLatestParser:
        'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.'
    }
  },

  /**
   * @param {RuleContext} context - The rule context.
   * @returns {RuleListener} AST event handlers.
   */
  create(context) {
    const options = context.options[0] || {}
    const ignoreProperties = options.ignoreProperties === true

    return {
      Program(node) {
        const sourceCode = context.getSourceCode()
        if (sourceCode.parserServices.getTemplateBodyTokenStore == null) {
          const filename = context.getFilename()
          if (path.extname(filename) === '.vue') {
            context.report({
              loc: { line: 1, column: 0 },
              messageId: 'useLatestParser'
            })
          }
          return
        }
        if (!node.templateBody) {
          return
        }
        const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
        const tokens = tokenStore.getTokens(node.templateBody, {
          includeComments: true
        })

        let prevToken = /** @type {Token} */ (tokens.shift())
        for (const token of tokens) {
          const spaces = token.range[0] - prevToken.range[1]
          const shouldIgnore =
            ignoreProperties &&
            (isProperty(context, token) || isProperty(context, prevToken))
          if (
            spaces > 1 &&
            token.loc.start.line === prevToken.loc.start.line &&
            !shouldIgnore
          ) {
            context.report({
              node: token,
              loc: {
                start: prevToken.loc.end,
                end: token.loc.start
              },
              messageId: 'multipleSpaces',
              fix: (fixer) =>
                fixer.replaceTextRange(
                  [prevToken.range[1], token.range[0]],
                  ' '
                ),
              data: {
                displayValue: sourceCode.getText(token)
              }
            })
          }
          prevToken = token
        }
      }
    }
  }
}


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

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

/**
 * count ObjectExpression element
 * @param {VDirective & {value: VExpressionContainer & {expression: ArrayExpression}}} node
 * @return {number}
 */
function countObjectExpression(node) {
  return node.value.expression.elements.filter(
    (element) => element && element.type === 'ObjectExpression'
  ).length
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow to pass multiple objects into array to class',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-multiple-objects-in-class.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: 'Unexpected multiple objects. Merge objects.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    return defineTemplateBodyVisitor(context, {
      /** @param {VDirective & {value: VExpressionContainer & {expression: ArrayExpression}}} node */
      'VAttribute[directive=true][key.argument.name="class"][key.name.name="bind"][value.expression.type="ArrayExpression"]'(
        node
      ) {
        if (countObjectExpression(node) > 1) {
          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')
const { findVariable } = require('@eslint-community/eslint-utils')

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow to pass multiple arguments to scoped slots',
      categories: ['vue3-recommended', 'vue2-recommended'],
      url: 'https://eslint.vuejs.org/rules/no-multiple-slot-args.html'
    },
    fixable: null,
    schema: [],
    messages: {
      unexpected: 'Unexpected multiple arguments.',
      unexpectedSpread: 'Unexpected spread argument.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * Verify the given node
     * @param {MemberExpression | Identifier} node The node to verify
     */
    function verify(node) {
      const parent = node.parent

      if (
        parent.type === 'VariableDeclarator' &&
        parent.id.type === 'Identifier'
      ) {
        // const foo = this.$scopedSlots.foo
        verifyReferences(parent.id)
        return
      }

      if (
        parent.type === 'AssignmentExpression' &&
        parent.right === node &&
        parent.left.type === 'Identifier'
      ) {
        // foo = this.$scopedSlots.foo
        verifyReferences(parent.left)
        return
      }

      if (parent.type !== 'CallExpression' || parent.arguments.includes(node)) {
        return
      }

      if (parent.arguments.length === 0) {
        return
      }
      if (parent.arguments.length > 1) {
        context.report({
          node: parent.arguments[1],
          messageId: 'unexpected'
        })
      }
      if (parent.arguments[0].type === 'SpreadElement') {
        context.report({
          node: parent.arguments[0],
          messageId: 'unexpectedSpread'
        })
      }
    }
    /**
     * Verify the references of the given node.
     * @param {Identifier} node The node to verify
     */
    function verifyReferences(node) {
      const variable = findVariable(utils.getScope(context, node), node)
      if (!variable) {
        return
      }
      for (const reference of variable.references) {
        if (!reference.isRead()) {
          continue
        }
        /** @type {Identifier} */
        const id = reference.identifier
        verify(id)
      }
    }

    return utils.defineVueVisitor(context, {
      /** @param {MemberExpression} node */
      MemberExpression(node) {
        const object = utils.skipChainExpression(node.object)
        if (object.type !== 'MemberExpression') {
          return
        }
        const name = utils.getStaticPropertyName(object)
        if (!name || (name !== '$slots' && name !== '$scopedSlots')) {
          return
        }
        if (!utils.isThis(object.object, context)) {
          return
        }
        verify(node)
      }
    })
  }
}


code:
/**
 * @fileoverview disallow adding multiple root nodes to the template
 * @author Przemyslaw Falowski (@przemkow)
 */
'use strict'

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

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow adding multiple root nodes to the template',
      categories: ['vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
    },
    fixable: null,
    schema: [],
    messages: {
      multipleRoot: 'The template root requires exactly one element.',
      textRoot: 'The template root requires an element rather than texts.',
      disallowedElement: "The template root disallows '<{{name}}>' elements.",
      disallowedDirective: "The template root disallows 'v-for' directives."
    }
  },
  /**
   * @param {RuleContext} context - The rule context.
   * @returns {RuleListener} AST event handlers.
   */
  create(context) {
    const sourceCode = context.getSourceCode()

    return {
      Program(program) {
        const element = program.templateBody
        if (element == null) {
          return
        }

        const rootElements = []
        let extraText = null
        let extraElement = null
        let vIf = false
        for (const child of element.children) {
          if (child.type === 'VElement') {
            if (rootElements.length === 0) {
              rootElements.push(child)
              vIf = utils.hasDirective(child, 'if')
            } else if (vIf && utils.hasDirective(child, 'else-if')) {
              rootElements.push(child)
            } else if (vIf && utils.hasDirective(child, 'else')) {
              rootElements.push(child)
              vIf = false
            } else {
              extraElement = child
            }
          } else if (sourceCode.getText(child).trim() !== '') {
            extraText = child
          }
        }

        if (extraText != null) {
          context.report({
            node: extraText,
            loc: extraText.loc,
            messageId: 'textRoot'
          })
        } else if (extraElement == null) {
          for (const element of rootElements) {
            const tag = element.startTag
            const name = element.name

            if (name === 'template' || name === 'slot') {
              context.report({
                node: tag,
                loc: tag.loc,
                messageId: 'disallowedElement',
                data: { name }
              })
            }
            if (utils.hasDirective(element, 'for')) {
              context.report({
                node: tag,
                loc: tag.loc,
                messageId: 'disallowedDirective'
              })
            }
          }
        } else {
          context.report({
            node: extraElement,
            loc: extraElement.loc,
            messageId: 'multipleRoot'
          })
        }
      }
    }
  }
}


code:
/**
 * @fileoverview disallow mutation component props
 * @author 2018 Armano
 */
'use strict'

/**
 * @typedef {{name?: string, set: Set<string>}} PropsInfo
 */

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

// https://github.com/vuejs/vue-next/blob/7c11c58faf8840ab97b6449c98da0296a60dddd8/packages/shared/src/globalsWhitelist.ts
const GLOBALS_WHITE_LISTED = new Set([
  'Infinity',
  'undefined',
  'NaN',
  'isFinite',
  'isNaN',
  'parseFloat',
  'parseInt',
  'decodeURI',
  'decodeURIComponent',
  'encodeURI',
  'encodeURIComponent',
  'Math',
  'Number',
  'Date',
  'Array',
  'Object',
  'Boolean',
  'String',
  'RegExp',
  'Map',
  'Set',
  'JSON',
  'Intl',
  'BigInt'
])

/**
 * @param {ASTNode} node
 * @returns {VExpressionContainer}
 */
function getVExpressionContainer(node) {
  let n = node
  while (n.type !== 'VExpressionContainer') {
    n = /** @type {ASTNode} */ (n.parent)
  }
  return n
}

/**
 * @param {ASTNode} node
 * @returns {node is Identifier}
 */
function isVmReference(node) {
  if (node.type !== 'Identifier') {
    return false
  }
  const parent = node.parent
  if (parent.type === 'MemberExpression') {
    if (parent.property === node) {
      // foo.id
      return false
    }
  } else if (
    parent.type === 'Property' &&
    parent.key === node &&
    !parent.computed
  ) {
    // {id: foo}
    return false
  }

  const exprContainer = getVExpressionContainer(node)

  for (const reference of exprContainer.references) {
    if (reference.variable != null) {
      // Not vm reference
      continue
    }
    if (reference.id === node) {
      return true
    }
  }
  return false
}

/**
 * @param { object } options
 * @param { boolean } options.shallowOnly Enables mutating the value of a prop but leaving the reference the same
 */
function parseOptions(options) {
  return Object.assign(
    {
      shallowOnly: false
    },
    options
  )
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow mutation of component props',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-mutating-props.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          shallowOnly: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      unexpectedMutation: 'Unexpected mutation of "{{key}}" prop.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const { shallowOnly } = parseOptions(context.options[0])
    /** @type {Map<ObjectExpression|CallExpression, PropsInfo>} */
    const propsMap = new Map()
    /** @type { { type: 'export' | 'mark' | 'definition', object: ObjectExpression } | { type: 'setup', object: CallExpression } | null } */
    let vueObjectData = null

    /**
     * @param {ASTNode} node
     * @param {string} name
     */
    function report(node, name) {
      context.report({
        node,
        messageId: 'unexpectedMutation',
        data: {
          key: name
        }
      })
    }

    /**
     * @param {MemberExpression|AssignmentProperty} node
     * @returns {string}
     */
    function getPropertyNameText(node) {
      const name = utils.getStaticPropertyName(node)
      if (name) {
        return name
      }
      if (node.computed) {
        const expr = node.type === 'Property' ? node.key : node.property
        const str = context.getSourceCode().getText(expr)
        return `[${str}]`
      }
      return '?unknown?'
    }

    /**
     * @param {MemberExpression|Identifier} props
     * @param {string} name
     * @param {boolean} isRootProps
     */
    function verifyMutating(props, name, isRootProps = false) {
      const invalid = utils.findMutating(props)
      if (invalid && isShallowOnlyInvalid(invalid, isRootProps)) {
        report(invalid.node, name)
      }
    }

    /**
     * @param {Pattern} param
     * @param {string[]} path
     * @returns {Generator<{ node: Identifier, path: string[] }>}
     */
    function* iteratePatternProperties(param, path) {
      if (!param) {
        return
      }
      switch (param.type) {
        case 'Identifier': {
          yield { node: param, path }
          break
        }
        case 'RestElement': {
          yield* iteratePatternProperties(param.argument, path)
          break
        }
        case 'AssignmentPattern': {
          yield* iteratePatternProperties(param.left, path)
          break
        }
        case 'ObjectPattern': {
          for (const prop of param.properties) {
            if (prop.type === 'Property') {
              const name = getPropertyNameText(prop)
              yield* iteratePatternProperties(prop.value, [...path, name])
            } else if (prop.type === 'RestElement') {
              yield* iteratePatternProperties(prop.argument, path)
            }
          }
          break
        }
        case 'ArrayPattern': {
          for (let index = 0; index < param.elements.length; index++) {
            const element = param.elements[index]
            if (element)
              yield* iteratePatternProperties(element, [...path, `${index}`])
          }
          break
        }
      }
    }

    /**
     * @param {Identifier} prop
     * @param {string[]} path
     */
    function verifyPropVariable(prop, path) {
      const variable = findVariable(utils.getScope(context, prop), prop)
      if (!variable) {
        return
      }

      for (const reference of variable.references) {
        if (!reference.isRead()) {
          continue
        }
        const id = reference.identifier

        const invalid = utils.findMutating(id)
        if (!invalid) {
          continue
        }
        let name
        if (!isShallowOnlyInvalid(invalid, path.length === 0)) {
          continue
        }
        if (path.length === 0) {
          if (invalid.pathNodes.length === 0) {
            continue
          }
          const mem = invalid.pathNodes[0]
          name = getPropertyNameText(mem)
        } else {
          if (invalid.pathNodes.length === 0 && invalid.kind !== 'call') {
            continue
          }
          name = path[0]
        }

        report(invalid.node, name)
      }
    }

    function* extractDefineVariableNames() {
      const globalScope = context.getSourceCode().scopeManager.globalScope
      if (globalScope) {
        for (const variable of globalScope.variables) {
          if (variable.defs.length > 0) {
            yield variable.name
          }
        }
        const moduleScope = globalScope.childScopes.find(
          (scope) => scope.type === 'module'
        )
        for (const variable of (moduleScope && moduleScope.variables) || []) {
          if (variable.defs.length > 0) {
            yield variable.name
          }
        }
      }
    }

    /**
     * Is shallowOnly false or the prop reassigned
     * @param {Exclude<ReturnType<typeof utils.findMutating>, null>} invalid
     * @param {boolean} isRootProps
     * @return {boolean}
     */
    function isShallowOnlyInvalid(invalid, isRootProps) {
      return (
        !shallowOnly ||
        (invalid.pathNodes.length === (isRootProps ? 1 : 0) &&
          ['assignment', 'update'].includes(invalid.kind))
      )
    }

    return utils.compositingVisitors(
      {},
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(node, props) {
          const defineVariableNames = new Set(extractDefineVariableNames())

          const propsInfo = {
            name: '',
            set: new Set(
              props
                .map((p) => p.propName)
                .filter(
                  /**
                   * @returns {propName is string}
                   */
                  (propName) =>
                    utils.isDef(propName) &&
                    !GLOBALS_WHITE_LISTED.has(propName) &&
                    !defineVariableNames.has(propName)
                )
            )
          }
          propsMap.set(node, propsInfo)
          vueObjectData = {
            type: 'setup',
            object: 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
          }

          for (const { node: prop, path } of iteratePatternProperties(
            target.parent.id,
            []
          )) {
            if (path.length === 0) {
              propsInfo.name = prop.name
            } else {
              propsInfo.set.add(prop.name)
            }
            verifyPropVariable(prop, path)
          }
        }
      }),
      utils.defineVueVisitor(context, {
        onVueObjectEnter(node) {
          propsMap.set(node, {
            set: new Set(
              utils
                .getComponentPropsFromOptions(node)
                .map((p) => p.propName)
                .filter(utils.isDef)
            )
          })
        },
        onVueObjectExit(node, { type }) {
          if (
            (!vueObjectData ||
              (vueObjectData.type !== 'export' &&
                vueObjectData.type !== 'setup')) &&
            type !== 'instance'
          ) {
            vueObjectData = {
              type,
              object: node
            }
          }
        },
        onSetupFunctionEnter(node) {
          const propsParam = node.params[0]
          if (!propsParam) {
            // no arguments
            return
          }
          if (
            propsParam.type === 'RestElement' ||
            propsParam.type === 'ArrayPattern'
          ) {
            // cannot check
            return
          }
          for (const { node: prop, path } of iteratePatternProperties(
            propsParam,
            []
          )) {
            verifyPropVariable(prop, path)
          }
        },
        /** @param {(Identifier | ThisExpression) & { parent: MemberExpression } } node */
        'MemberExpression > :matches(Identifier, ThisExpression)'(
          node,
          { node: vueNode }
        ) {
          if (!utils.isThis(node, context)) {
            return
          }
          const mem = node.parent
          if (mem.object !== node) {
            return
          }
          const name = utils.getStaticPropertyName(mem)
          if (
            name &&
            /** @type {PropsInfo} */ (propsMap.get(vueNode)).set.has(name)
          ) {
            verifyMutating(mem, name)
          }
        }
      }),
      utils.defineTemplateBodyVisitor(context, {
        /** @param {ThisExpression & { parent: MemberExpression } } node */
        'VExpressionContainer MemberExpression > ThisExpression'(node) {
          if (!vueObjectData) {
            return
          }
          const mem = node.parent
          if (mem.object !== node) {
            return
          }
          const name = utils.getStaticPropertyName(mem)
          if (
            name &&
            /** @type {PropsInfo} */ (
              propsMap.get(vueObjectData.object)
            ).set.has(name)
          ) {
            verifyMutating(mem, name)
          }
        },
        /** @param {Identifier } node */
        'VExpressionContainer Identifier'(node) {
          if (!vueObjectData) {
            return
          }
          if (!isVmReference(node)) {
            return
          }
          const propsInfo = /** @type {PropsInfo} */ (
            propsMap.get(vueObjectData.object)
          )
          const isRootProps = !!node.name && propsInfo.name === node.name
          const parent = node.parent
          const name =
            (isRootProps &&
              parent.type === 'MemberExpression' &&
              utils.getStaticPropertyName(parent)) ||
            node.name
          if (name && (propsInfo.set.has(name) || isRootProps)) {
            verifyMutating(node, name, isRootProps)
          }
        },
        /** @param {ESNode} node */
        "VAttribute[directive=true]:matches([key.name.name='model'], [key.name.name='bind']) VExpressionContainer > *"(
          node
        ) {
          if (!vueObjectData) {
            return
          }
          let attr = node.parent
          while (attr && attr.type !== 'VAttribute') {
            attr = attr.parent
          }
          if (
            attr &&
            attr.directive &&
            attr.key.name.name === 'bind' &&
            !attr.key.modifiers.some((mod) => mod.name === 'sync')
          ) {
            return
          }

          const propsInfo = /** @type {PropsInfo} */ (
            propsMap.get(vueObjectData.object)
          )

          const nodes = utils.getMemberChaining(node)
          const first = nodes[0]
          let name
          if (isVmReference(first)) {
            if (first.name === propsInfo.name) {
              // props variable
              if (shallowOnly && nodes.length > 2) {
                return
              }
              name = (nodes[1] && getPropertyNameText(nodes[1])) || first.name
            } else {
              if (shallowOnly && nodes.length > 1) {
                return
              }
              name = first.name
              if (!name || !propsInfo.set.has(name)) {
                return
              }
            }
          } else if (first.type === 'ThisExpression') {
            if (shallowOnly && nodes.length > 2) {
              return
            }
            const mem = nodes[1]
            if (!mem) {
              return
            }
            name = utils.getStaticPropertyName(mem)
            if (!name || !propsInfo.set.has(name)) {
              return
            }
          } else {
            return
          }
          report(node, name)
        }
      })
    )
  }
}


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

// https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
const DEFAULT_OPTIONS = Object.freeze(
  Object.assign(Object.create(null), {
    'abrupt-closing-of-empty-comment': true,
    'absence-of-digits-in-numeric-character-reference': true,
    'cdata-in-html-content': true,
    'character-reference-outside-unicode-range': true,
    'control-character-in-input-stream': true,
    'control-character-reference': true,
    'eof-before-tag-name': true,
    'eof-in-cdata': true,
    'eof-in-comment': true,
    'eof-in-tag': true,
    'incorrectly-closed-comment': true,
    'incorrectly-opened-comment': true,
    'invalid-first-character-of-tag-name': true,
    'missing-attribute-value': true,
    'missing-end-tag-name': true,
    'missing-semicolon-after-character-reference': true,
    'missing-whitespace-between-attributes': true,
    'nested-comment': true,
    'noncharacter-character-reference': true,
    'noncharacter-in-input-stream': true,
    'null-character-reference': true,
    'surrogate-character-reference': true,
    'surrogate-in-input-stream': true,
    'unexpected-character-in-attribute-name': true,
    'unexpected-character-in-unquoted-attribute-value': true,
    'unexpected-equals-sign-before-attribute-name': true,
    'unexpected-null-character': true,
    'unexpected-question-mark-instead-of-tag-name': true,
    'unexpected-solidus-in-tag': true,
    'unknown-named-character-reference': true,
    'end-tag-with-attributes': true,
    'duplicate-attribute': true,
    'end-tag-with-trailing-solidus': true,
    'non-void-html-element-start-tag-with-trailing-solidus': false,
    'x-invalid-end-tag': true,
    'x-invalid-namespace': true
  })
)

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow parsing errors in `<template>`',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-parsing-error.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: Object.fromEntries(
          Object.keys(DEFAULT_OPTIONS).map((code) => [
            code,
            { type: 'boolean' }
          ])
        ),
        additionalProperties: false
      }
    ],
    messages: {
      parsingError: 'Parsing error: {{message}}.'
    }
  },
  /**
   * @param {RuleContext} context - The rule context.
   * @returns {RuleListener} AST event handlers.
   */
  create(context) {
    const options = Object.assign({}, DEFAULT_OPTIONS, context.options[0] || {})

    return {
      Program(program) {
        const node = program.templateBody
        if (node == null || node.errors == null) {
          return
        }

        for (const error of node.errors) {
          if (error.code && !options[error.code]) {
            continue
          }

          context.report({
            node,
            loc: { line: error.lineNumber, column: error.column },
            messageId: 'parsingError',
            data: {
              message: error.message.endsWith('.')
                ? error.message.slice(0, -1)
                : error.message
            }
          })
        }
      }
    }
  }
}


code:
/**
 * @fileoverview detect if there is a potential typo in your component property
 * @author IWANABETHATGUY
 */
'use strict'

const utils = require('../utils')
const vueComponentOptions = require('../utils/vue-component-options.json')
module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow a potential typo in your component property',
      categories: undefined,
      recommended: false,
      url: 'https://eslint.vuejs.org/rules/no-potential-component-option-typo.html'
    },
    fixable: null,
    hasSuggestions: true,
    schema: [
      {
        type: 'object',
        properties: {
          presets: {
            type: 'array',
            items: {
              type: 'string',
              enum: ['all', 'vue', 'vue-router', 'nuxt']
            },
            uniqueItems: true,
            minItems: 0
          },
          custom: {
            type: 'array',
            minItems: 0,
            items: { type: 'string' },
            uniqueItems: true
          },
          threshold: {
            type: 'number',
            minimum: 1
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      potentialTypo: `'{{name}}' may be a typo, which is similar to option [{{option}}].`
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const option = context.options[0] || {}
    const custom = option.custom || []
    /** @type {('all' | 'vue' | 'vue-router' | 'nuxt')[]} */
    const presets = option.presets || ['vue']
    const threshold = option.threshold || 1
    /** @type {Set<string>} */
    const candidateOptionSet = new Set(custom)
    for (const preset of presets) {
      if (preset === 'all') {
        for (const opts of Object.values(vueComponentOptions)) {
          for (const opt of opts) {
            candidateOptionSet.add(opt)
          }
        }
      } else {
        for (const opt of vueComponentOptions[preset]) {
          candidateOptionSet.add(opt)
        }
      }
    }
    const candidateOptionList = [...candidateOptionSet]
    if (candidateOptionList.length === 0) {
      return {}
    }
    return utils.executeOnVue(context, (obj) => {
      const componentInstanceOptions = obj.properties
        .map((p) => {
          if (p.type === 'Property') {
            const name = utils.getStaticPropertyName(p)
            if (name != null) {
              return {
                name,
                key: p.key
              }
            }
          }
          return null
        })
        .filter(utils.isDef)

      if (componentInstanceOptions.length === 0) {
        return
      }
      for (const option of componentInstanceOptions) {
        const id = option.key
        const name = option.name
        if (candidateOptionSet.has(name)) {
          continue
        }
        const potentialTypoList = candidateOptionList
          .map((o) => ({ option: o, distance: utils.editDistance(o, name) }))
          .filter(({ distance }) => distance <= threshold && distance > 0)
          .sort((a, b) => a.distance - b.distance)
        if (potentialTypoList.length > 0) {
          context.report({
            node: id,
            messageId: 'potentialTypo',
            data: {
              name,
              option: potentialTypoList.map(({ option }) => option).join(',')
            },
            suggest: potentialTypoList.map(({ option }) => ({
              desc: `Replace property '${name}' to '${option}'`,
              fix(fixer) {
                return fixer.replaceText(id, option)
              }
            }))
          })
        }
      }
    })
  }
}


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

const { extractRefObjectReferences } = require('../utils/ref-object-references')
const utils = require('../utils')

/**
 * @typedef {import('../utils/ref-object-references').RefObjectReferences} RefObjectReferences
 * @typedef {import('../utils/ref-object-references').RefObjectReferenceForIdentifier} RefObjectReferenceForIdentifier
 */

/**
 * Checks whether the given identifier reference has been initialized with a ref object.
 * @param {RefObjectReferenceForIdentifier | null} data
 * @returns {data is RefObjectReferenceForIdentifier}
 */
function isRefInit(data) {
  const init = data && data.variableDeclarator && data.variableDeclarator.init
  if (!init) {
    return false
  }
  return data.defineChain.includes(/** @type {any} */ (init))
}
module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'disallow use of value wrapped by `ref()` (Composition API) as an operand',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-ref-as-operand.html'
    },
    fixable: 'code',
    schema: [],
    messages: {
      requireDotValue:
        'Must use `.value` to read or write the value wrapped by `{{method}}()`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {RefObjectReferences} */
    let refReferences

    /**
     * @param {Identifier} node
     */
    function reportIfRefWrapped(node) {
      const data = refReferences.get(node)
      if (!isRefInit(data)) {
        return
      }
      context.report({
        node,
        messageId: 'requireDotValue',
        data: {
          method: data.method
        },
        fix(fixer) {
          return fixer.insertTextAfter(node, '.value')
        }
      })
    }
    return {
      Program() {
        refReferences = extractRefObjectReferences(context)
      },
      // if (refValue)
      /** @param {Identifier} node */
      'IfStatement>Identifier'(node) {
        reportIfRefWrapped(node)
      },
      // switch (refValue)
      /** @param {Identifier} node */
      'SwitchStatement>Identifier'(node) {
        reportIfRefWrapped(node)
      },
      // -refValue, +refValue, !refValue, ~refValue, typeof refValue
      /** @param {Identifier} node */
      'UnaryExpression>Identifier'(node) {
        reportIfRefWrapped(node)
      },
      // refValue++, refValue--
      /** @param {Identifier} node */
      'UpdateExpression>Identifier'(node) {
        reportIfRefWrapped(node)
      },
      // refValue+1, refValue-1
      /** @param {Identifier} node */
      'BinaryExpression>Identifier'(node) {
        reportIfRefWrapped(node)
      },
      // refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
      /** @param {Identifier & {parent: AssignmentExpression}} node */
      'AssignmentExpression>Identifier'(node) {
        if (node.parent.operator === '=' && node.parent.left !== node) {
          return
        }
        reportIfRefWrapped(node)
      },
      // refValue || other, refValue && other. ignore: other || refValue
      /** @param {Identifier & {parent: LogicalExpression}} node */
      'LogicalExpression>Identifier'(node) {
        if (node.parent.left !== node) {
          return
        }
        // Report only constants.
        const data = refReferences.get(node)
        if (
          !data ||
          !data.variableDeclaration ||
          data.variableDeclaration.kind !== 'const'
        ) {
          return
        }
        reportIfRefWrapped(node)
      },
      // refValue ? x : y
      /** @param {Identifier & {parent: ConditionalExpression}} node */
      'ConditionalExpression>Identifier'(node) {
        if (node.parent.test !== node) {
          return
        }
        reportIfRefWrapped(node)
      },
      // `${refValue}`
      /** @param {Identifier} node */
      'TemplateLiteral>Identifier'(node) {
        reportIfRefWrapped(node)
      },
      // refValue.x
      /** @param {Identifier & {parent: MemberExpression}} node */
      'MemberExpression>Identifier'(node) {
        if (node.parent.object !== node) {
          return
        }
        const name = utils.getStaticPropertyName(node.parent)
        if (
          name === 'value' ||
          name == null ||
          // WritableComputedRef
          name === 'effect'
        ) {
          return
        }
        reportIfRefWrapped(node)
      }
    }
  }
}


code:
/**
 * @author Yosuke Ota
 * See LICENSE file in root directory for full license.
 */
'use strict'
const baseRule = require('./no-ref-object-reactivity-loss')

module.exports = {
  // eslint-disable-next-line eslint-plugin/require-meta-schema, eslint-plugin/prefer-message-ids, internal/no-invalid-meta, eslint-plugin/require-meta-type -- inherit schema from base rule
  meta: {
    ...baseRule.meta,
    // eslint-disable-next-line eslint-plugin/require-meta-docs-description, internal/no-invalid-meta-docs-categories, eslint-plugin/meta-property-ordering
    docs: {
      ...baseRule.meta.docs,
      url: 'https://eslint.vuejs.org/rules/no-ref-object-destructure.html'
    },
    deprecated: true,
    replacedBy: ['no-ref-object-reactivity-loss']
  },
  /** @param {RuleContext} context */
  create(context) {
    return baseRule.create(context)
  }
}


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

const utils = require('../utils')
const {
  extractRefObjectReferences,
  extractReactiveVariableReferences
} = require('../utils/ref-object-references')

/**
 * @typedef {import('../utils/ref-object-references').RefObjectReferences} RefObjectReferences
 * @typedef {import('../utils/ref-object-references').RefObjectReference} RefObjectReference
 */

/**
 * Checks whether writing assigns a value to the given pattern.
 * @param {Pattern | AssignmentProperty | Property} node
 * @returns {boolean}
 */
function isUpdate(node) {
  const parent = node.parent
  if (parent.type === 'UpdateExpression' && parent.argument === node) {
    // e.g. `pattern++`
    return true
  }
  if (parent.type === 'AssignmentExpression' && parent.left === node) {
    // e.g. `pattern = 42`
    return true
  }
  if (
    (parent.type === 'Property' && parent.value === node) ||
    parent.type === 'ArrayPattern' ||
    (parent.type === 'ObjectPattern' &&
      parent.properties.includes(/** @type {any} */ (node))) ||
    (parent.type === 'AssignmentPattern' && parent.left === node) ||
    parent.type === 'RestElement' ||
    (parent.type === 'MemberExpression' && parent.object === node)
  ) {
    return isUpdate(parent)
  }
  return false
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description:
        'disallow usages of ref objects that can lead to loss of reactivity',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-ref-object-reactivity-loss.html'
    },
    fixable: null,
    schema: [],
    messages: {
      getValueInSameScope:
        'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
      getReactiveVariableInSameScope:
        'Getting a reactive variable in the same scope will cause the value to lose reactivity.'
    }
  },
  /**
   * @param {RuleContext} context
   * @returns {RuleListener}
   */
  create(context) {
    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {Program | FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
     */
    /** @type {ScopeStack} */
    let scopeStack = { upper: null, node: context.getSourceCode().ast }
    /** @type {Map<CallExpression, ScopeStack>} */
    const scopes = new Map()

    const refObjectReferences = extractRefObjectReferences(context)
    const reactiveVariableReferences =
      extractReactiveVariableReferences(context)

    /**
     * Verify the given ref object value. `refObj = ref(); refObj.value;`
     * @param {Expression | Super | ObjectPattern} node
     */
    function verifyRefObjectValue(node) {
      const ref = refObjectReferences.get(node)
      if (!ref) {
        return
      }
      if (scopes.get(ref.define) !== scopeStack) {
        // Not in the same scope
        return
      }

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

    /**
     * Verify the given reactive variable. `refVal = $ref(); refVal;`
     * @param {Identifier} node
     */
    function verifyReactiveVariable(node) {
      const ref = reactiveVariableReferences.get(node)
      if (!ref || ref.escape) {
        return
      }
      if (scopes.get(ref.define) !== scopeStack) {
        // Not in the same scope
        return
      }

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

    return {
      ':function'(node) {
        scopeStack = { upper: scopeStack, node }
      },
      ':function:exit'() {
        scopeStack = scopeStack.upper || scopeStack
      },
      CallExpression(node) {
        scopes.set(node, scopeStack)
      },
      /**
       * Check for `refObj.value`.
       */
      'MemberExpression:exit'(node) {
        if (isUpdate(node)) {
          // e.g. `refObj.value = 42`, `refObj.value++`
          return
        }
        const name = utils.getStaticPropertyName(node)
        if (name !== 'value') {
          return
        }
        verifyRefObjectValue(node.object)
      },
      /**
       * Check for `{value} = refObj`.
       */
      'ObjectPattern:exit'(node) {
        const prop = utils.findAssignmentProperty(node, 'value')
        if (!prop) {
          return
        }
        verifyRefObjectValue(node)
      },
      /**
       * Check for reactive variable`.
       * @param {Identifier} node
       */
      'Identifier:exit'(node) {
        if (isUpdate(node)) {
          // e.g. `reactiveVariable = 42`, `reactiveVariable++`
          return
        }
        verifyReactiveVariable(node)
      }
    }
  }
}


code:
/**
 * @author @neferqiqi
 * See LICENSE file in root directory for full license.
 */
'use strict'
const utils = require('../utils')
/**
 * @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
 * @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
 * @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
 * @typedef {import('../utils').ComponentUnknownProp} ComponentUnknownProp
 * @typedef {import('../utils').ComponentProp} ComponentProp
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce props with default values to be optional',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-required-prop-with-default.html'
    },
    fixable: 'code',
    hasSuggestions: true,
    schema: [
      {
        type: 'object',
        properties: {
          autofix: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      requireOptional: `Prop "{{ key }}" should be optional.`,
      fixRequiredProp: `Change this prop to be optional.`
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    let canAutoFix = false
    const option = context.options[0]
    if (option) {
      canAutoFix = option.autofix
    }

    /**
     * @param {ComponentProp} prop
     * @param {Set<string>} [defaultProps]
     **/
    const handleObjectProp = (prop, defaultProps) => {
      if (
        prop.type === 'object' &&
        prop.propName &&
        prop.value.type === 'ObjectExpression' &&
        (utils.findProperty(prop.value, 'default') ||
          defaultProps?.has(prop.propName))
      ) {
        const requiredProperty = utils.findProperty(prop.value, 'required')
        if (!requiredProperty) return
        const requiredNode = requiredProperty.value
        if (
          requiredNode &&
          requiredNode.type === 'Literal' &&
          !!requiredNode.value
        ) {
          context.report({
            node: prop.node,
            loc: prop.node.loc,
            data: {
              key: prop.propName
            },
            messageId: 'requireOptional',
            fix: canAutoFix
              ? (fixer) => fixer.replaceText(requiredNode, 'false')
              : null,
            suggest: canAutoFix
              ? null
              : [
                  {
                    messageId: 'fixRequiredProp',
                    fix: (fixer) => fixer.replaceText(requiredNode, 'false')
                  }
                ]
          })
        }
      } else if (
        prop.type === 'type' &&
        defaultProps?.has(prop.propName) &&
        prop.required
      ) {
        // skip setter & getter case
        if (
          prop.node.type === 'TSMethodSignature' &&
          (prop.node.kind === 'get' || prop.node.kind === 'set')
        ) {
          return
        }
        // skip computed
        if (prop.node.computed) {
          return
        }
        context.report({
          node: prop.node,
          loc: prop.node.loc,
          data: {
            key: prop.propName
          },
          messageId: 'requireOptional',
          fix: canAutoFix
            ? (fixer) => fixer.insertTextAfter(prop.key, '?')
            : null,
          suggest: canAutoFix
            ? null
            : [
                {
                  messageId: 'fixRequiredProp',
                  fix: (fixer) => fixer.insertTextAfter(prop.key, '?')
                }
              ]
        })
      }
    }

    return utils.compositingVisitors(
      utils.defineVueVisitor(context, {
        onVueObjectEnter(node) {
          utils
            .getComponentPropsFromOptions(node)
            .map((prop) => handleObjectProp(prop))
        }
      }),
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(node, props) {
          const defaultProps = new Set([
            ...Object.keys(utils.getWithDefaultsPropExpressions(node)),
            ...Object.keys(
              utils.getDefaultPropExpressionsForPropsDestructure(node)
            )
          ])
          props.map((prop) => handleObjectProp(prop, defaultProps))
        }
      })
    )
  }
}


code:
/**
 * @fileoverview disallow the use of reserved names in component definitions
 * @author Jake Hassel <https://github.com/shadskii>
 */
'use strict'

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

const htmlElements = require('../utils/html-elements.json')
const deprecatedHtmlElements = require('../utils/deprecated-html-elements.json')
const svgElements = require('../utils/svg-elements.json')
const RESERVED_NAMES_IN_VUE = new Set(
  require('../utils/vue2-builtin-components')
)

const RESERVED_NAMES_IN_VUE3 = new Set(
  require('../utils/vue3-builtin-components')
)

const kebabCaseElements = [
  'annotation-xml',
  'color-profile',
  'font-face',
  'font-face-src',
  'font-face-uri',
  'font-face-format',
  'font-face-name',
  'missing-glyph'
]

/** @param {string} word  */
function isLowercase(word) {
  return /^[a-z]*$/.test(word)
}

const RESERVED_NAMES_IN_HTML = new Set([
  ...htmlElements,
  ...htmlElements.map(casing.capitalize)
])
const RESERVED_NAMES_IN_OTHERS = new Set([
  ...deprecatedHtmlElements,
  ...deprecatedHtmlElements.map(casing.capitalize),
  ...kebabCaseElements,
  ...kebabCaseElements.map(casing.pascalCase),
  ...svgElements,
  ...svgElements.filter(isLowercase).map(casing.capitalize)
])

/**
 * @param {Expression | SpreadElement} node
 * @returns {node is (Literal | TemplateLiteral)}
 */
function canVerify(node) {
  return (
    node.type === 'Literal' ||
    (node.type === 'TemplateLiteral' &&
      node.expressions.length === 0 &&
      node.quasis.length === 1)
  )
}

/**
 * @param {string} name
 * @returns {string}
 */
function getMessageId(name) {
  if (RESERVED_NAMES_IN_HTML.has(name)) return 'reservedInHtml'
  if (RESERVED_NAMES_IN_VUE.has(name)) return 'reservedInVue'
  if (RESERVED_NAMES_IN_VUE3.has(name)) return 'reservedInVue3'
  return 'reserved'
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'disallow the use of reserved names in component definitions',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          disallowVueBuiltInComponents: {
            type: 'boolean'
          },
          disallowVue3BuiltInComponents: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      reserved: 'Name "{{name}}" is reserved.',
      reservedInHtml: 'Name "{{name}}" is reserved in HTML.',
      reservedInVue: 'Name "{{name}}" is reserved in Vue.js.',
      reservedInVue3: 'Name "{{name}}" is reserved in Vue.js 3.x.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    const disallowVueBuiltInComponents =
      options.disallowVueBuiltInComponents === true
    const disallowVue3BuiltInComponents =
      options.disallowVue3BuiltInComponents === true

    const reservedNames = new Set([
      ...RESERVED_NAMES_IN_HTML,
      ...(disallowVueBuiltInComponents ? RESERVED_NAMES_IN_VUE : []),
      ...(disallowVue3BuiltInComponents ? RESERVED_NAMES_IN_VUE3 : []),
      ...RESERVED_NAMES_IN_OTHERS
    ])

    /**
     * @param {Literal | TemplateLiteral} node
     */
    function reportIfInvalid(node) {
      let name
      if (node.type === 'TemplateLiteral') {
        const quasis = node.quasis[0]
        name = quasis.value.cooked
      } else {
        name = `${node.value}`
      }
      if (reservedNames.has(name)) {
        report(node, name)
      }
    }

    /**
     * @param {ESNode} node
     * @param {string} name
     */
    function report(node, name) {
      context.report({
        node,
        messageId: getMessageId(name),
        data: {
          name
        }
      })
    }

    return utils.compositingVisitors(
      utils.executeOnCallVueComponent(context, (node) => {
        if (node.arguments.length === 2) {
          const argument = node.arguments[0]

          if (canVerify(argument)) {
            reportIfInvalid(argument)
          }
        }
      }),
      utils.executeOnVue(context, (obj) => {
        // Report if a component has been registered locally with a reserved name.
        for (const { node, name } of utils.getRegisteredComponents(obj)) {
          if (reservedNames.has(name)) {
            report(node, name)
          }
        }

        const node = utils.findProperty(obj, 'name')

        if (!node) return
        if (!canVerify(node.value)) return
        reportIfInvalid(node.value)
      }),
      utils.defineScriptSetupVisitor(context, {
        onDefineOptionsEnter(node) {
          if (node.arguments.length === 0) return
          const define = node.arguments[0]
          if (define.type !== 'ObjectExpression') return
          const nameNode = utils.findProperty(define, 'name')
          if (!nameNode) return
          if (!canVerify(nameNode.value)) return
          reportIfInvalid(nameNode.value)
        }
      })
    )
  }
}


code:
/**
 * @fileoverview Prevent overwrite reserved keys
 * @author Armano
 */
'use strict'

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

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

const RESERVED_KEYS = require('../utils/vue-reserved.json')
/** @type {GroupName[]} */
const GROUP_NAMES = [
  'props',
  'computed',
  'data',
  'asyncData',
  'methods',
  'setup'
]

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow overwriting reserved keys',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-reserved-keys.html'
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          reserved: {
            type: 'array'
          },
          groups: {
            type: 'array'
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      reserved: "Key '{{name}}' is reserved.",
      startsWithUnderscore:
        "Keys starting with '_' are reserved in '{{name}}' group."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    const reservedKeys = new Set([
      ...RESERVED_KEYS,
      ...(options.reserved || [])
    ])
    const groups = new Set([...GROUP_NAMES, ...(options.groups || [])])

    return utils.compositingVisitors(
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(_node, props) {
          for (const prop of props) {
            if (prop.propName && reservedKeys.has(prop.propName)) {
              const { propName, node } = prop
              context.report({
                node,
                messageId: 'reserved',
                data: {
                  name: propName
                }
              })
            }
          }
        }
      }),
      utils.executeOnVue(context, (obj) => {
        const properties = utils.iterateProperties(obj, groups)
        for (const o of properties) {
          if (
            (o.groupName === 'data' || o.groupName === 'asyncData') &&
            o.name[0] === '_'
          ) {
            context.report({
              node: o.node,
              messageId: 'startsWithUnderscore',
              data: {
                name: o.name
              }
            })
          } else if (reservedKeys.has(o.name)) {
            context.report({
              node: o.node,
              messageId: 'reserved',
              data: {
                name: o.name
              }
            })
          }
        }
      })
    )
  }
}


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

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

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

const RESERVED = {
  3: ['key', 'ref'],
  2: ['key', 'ref', 'is', 'slot', 'slot-scope', 'slotScope', 'class', 'style']
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow reserved names in props',
      categories: ['vue3-essential', 'vue2-essential'],
      url: 'https://eslint.vuejs.org/rules/no-reserved-props.html',
      defaultOptions: {
        vue2: [{ vueVersion: 2 }]
      }
    },
    fixable: null,
    schema: [
      {
        type: 'object',
        properties: {
          vueVersion: {
            enum: [2, 3]
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      reserved:
        "'{{propName}}' is a reserved attribute and cannot be used as props."
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const options = context.options[0] || {}
    /** @type {2|3} */
    const vueVersion = options.vueVersion || 3

    const reserved = new Set(RESERVED[vueVersion])

    /**
     * @param {ComponentProp[]} props
     */
    function processProps(props) {
      for (const prop of props) {
        if (prop.propName != null && reserved.has(prop.propName)) {
          context.report({
            node: prop.node,
            messageId: `reserved`,
            data: {
              propName: casing.kebabCase(prop.propName)
            }
          })
        }
      }
    }

    return utils.compositingVisitors(
      utils.defineScriptSetupVisitor(context, {
        onDefinePropsEnter(_node, props) {
          processProps(props)
        }
      }),
      utils.executeOnVue(context, (obj) => {
        processProps(utils.getComponentPropsFromOptions(obj))
      })
    )
  }
}


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

const utils = require('../utils')
const regexp = require('../utils/regexp')
/**
 * @typedef {object} ParsedOption
 * @property { (block: VElement) => boolean } test
 * @property {string} [message]
 */

/**
 * @param {string} str
 * @returns {(str: string) => boolean}
 */
function buildMatcher(str) {
  if (regexp.isRegExp(str)) {
    const re = regexp.toRegExp(str)
    return (s) => {
      re.lastIndex = 0
      return re.test(s)
    }
  }
  return (s) => s === str
}
/**
 * @param {any} option
 * @returns {ParsedOption}
 */
function parseOption(option) {
  if (typeof option === 'string') {
    const matcher = buildMatcher(option)
    return {
      test(block) {
        return matcher(block.rawName)
      }
    }
  }
  const parsed = parseOption(option.element)
  parsed.message = option.message
  return parsed
}

/**
 * @param {VElement} block
 */
function defaultMessage(block) {
  return `Using \`<${block.rawName}>\` is not allowed.`
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow specific block',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-restricted-block.html'
    },
    fixable: null,
    schema: {
      type: 'array',
      items: {
        oneOf: [
          { type: 'string' },
          {
            type: 'object',
            properties: {
              element: { type: 'string' },
              message: { type: 'string', minLength: 1 }
            },
            required: ['element'],
            additionalProperties: false
          }
        ]
      },
      uniqueItems: true,
      minItems: 0
    },

    messages: {
      // eslint-disable-next-line eslint-plugin/report-message-format
      restrictedBlock: '{{message}}'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {ParsedOption[]} */
    const options = context.options.map(parseOption)

    const sourceCode = context.getSourceCode()
    const documentFragment =
      sourceCode.parserServices.getDocumentFragment &&
      sourceCode.parserServices.getDocumentFragment()

    function getTopLevelHTMLElements() {
      if (documentFragment) {
        return documentFragment.children.filter(utils.isVElement)
      }
      return []
    }

    return {
      /** @param {Program} node */
      Program(node) {
        if (utils.hasInvalidEOF(node)) {
          return
        }
        for (const block of getTopLevelHTMLElements()) {
          for (const option of options) {
            if (option.test(block)) {
              const message = option.message || defaultMessage(block)
              context.report({
                node: block.startTag,
                messageId: 'restrictedBlock',
                data: { message }
              })
              break
            }
          }
        }
      }
    }
  }
}


code:
/**
 * @author Yosuke Ota
 * See LICENSE file in root directory for full license.
 */
'use strict'
const fs = require('fs')
const path = require('path')
const { ReferenceTracker } = require('@eslint-community/eslint-utils')
const utils = require('../utils')

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

/**
 * @param {string} id
 */
function safeRequireResolve(id) {
  try {
    if (fs.statSync(id).isDirectory()) {
      return require.resolve(id)
    }
  } catch (_error) {
    // ignore
  }
  return id
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow asynchronously called restricted methods',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-restricted-call-after-await.html'
    },
    fixable: null,
    schema: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          module: { type: 'string' },
          path: {
            anyOf: [
              { type: 'string' },
              {
                type: 'array',
                items: {
                  type: 'string'
                }
              }
            ]
          },
          message: { type: 'string', minLength: 1 }
        },
        required: ['module'],
        additionalProperties: false
      },
      uniqueItems: true,
      minItems: 0
    },
    messages: {
      // eslint-disable-next-line eslint-plugin/report-message-format
      restricted: '{{message}}'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /**
     * @typedef {object} SetupScopeData
     * @property {boolean} afterAwait
     * @property {[number,number]} range
     */

    /** @type {Map<ESNode, string>} */
    const restrictedCallNodes = new Map()
    /** @type {Map<FunctionExpression | ArrowFunctionExpression | FunctionDeclaration, SetupScopeData>} */
    const setupScopes = new Map()

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

    /** @type {Record<string, string[]> | null} */
    let allLocalImports = null

    /**
     * @param {Program} ast
     */
    function getAllLocalImports(ast) {
      if (!allLocalImports) {
        allLocalImports = {}
        const dir = path.dirname(context.getFilename())
        for (const body of ast.body) {
          if (body.type !== 'ImportDeclaration') {
            continue
          }
          const source = String(body.source.value)
          if (!source.startsWith('.')) {
            continue
          }
          const modulePath = safeRequireResolve(path.join(dir, source))
          const list =
            allLocalImports[modulePath] || (allLocalImports[modulePath] = [])
          list.push(source)
        }
      }

      return allLocalImports
    }

    function getCwd() {
      if (context.getCwd) {
        return context.getCwd()
      }
      return path.resolve('')
    }

    /**
     * @param {string} moduleName
     * @param {Program} ast
     * @returns {string[]}
     */
    function normalizeModules(moduleName, ast) {
      /** @type {string} */
      let modulePath
      if (moduleName.startsWith('.')) {
        modulePath = safeRequireResolve(path.join(getCwd(), moduleName))
      } else if (path.isAbsolute(moduleName)) {
        modulePath = safeRequireResolve(moduleName)
      } else {
        return [moduleName]
      }
      return getAllLocalImports(ast)[modulePath] || []
    }

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

          for (const option of context.options) {
            const modules = normalizeModules(option.module, node)

            for (const module of modules) {
              /** @type {TraceMap} */
              const traceMap = {
                [module]: {
                  [ReferenceTracker.ESM]: true
                }
              }

              /** @type {TraceKind & TraceMap} */
              const mod = traceMap[module]
              let local = mod
              const paths = Array.isArray(option.path)
                ? option.path
                : [option.path || 'default']
              for (const path of paths) {
                local = local[path] || (local[path] = {})
              }
              local[ReferenceTracker.CALL] = true
              const message =
                option.message ||
                `\`${[`import("${module}")`, ...paths].join(
                  '.'
                )}\` is forbidden after an \`await\` expression.`

              for (const { node } of tracker.iterateEsmReferences(traceMap)) {
                restrictedCallNodes.set(node, message)
              }
            }
          }
        }
      },
      utils.defineVueVisitor(context, {
        onSetupFunctionEnter(node) {
          setupScopes.set(node, {
            afterAwait: false,
            range: node.range
          })
        },
        /** @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} 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 message = restrictedCallNodes.get(node)
          if (message) {
            context.report({
              node,
              messageId: 'restricted',
              data: { message }
            })
          }
        },
        onSetupFunctionExit(node) {
          setupScopes.delete(node)
        }
      })
    )
  }
}


code:
/**
 * @fileoverview Forbid certain classes from being used
 * @author Tao Bojlen
 */
'use strict'

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

/**
 * Report a forbidden class
 * @param {string} className
 * @param {*} node
 * @param {RuleContext} context
 * @param {Set<string>} forbiddenClasses
 * @param {Array<RegExp>} forbiddenClassesRegexps
 */
const reportForbiddenClass = (
  className,
  node,
  context,
  forbiddenClasses,
  forbiddenClassesRegexps
) => {
  if (
    forbiddenClasses.has(className) ||
    forbiddenClassesRegexps.some((re) => re.test(className))
  ) {
    const loc = node.value ? node.value.loc : node.loc
    context.report({
      node,
      loc,
      messageId: 'forbiddenClass',
      data: {
        class: className
      }
    })
  }
}

/**
 * @param {Expression} node
 * @param {boolean} [textOnly]
 * @returns {IterableIterator<{ className:string, reportNode: ESNode }>}
 */
function* extractClassNames(node, textOnly) {
  if (node.type === 'Literal') {
    yield* `${node.value}`
      .split(/\s+/)
      .map((className) => ({ className, reportNode: node }))
    return
  }
  if (node.type === 'TemplateLiteral') {
    for (const templateElement of node.quasis) {
      yield* templateElement.value.cooked
        .split(/\s+/)
        .map((className) => ({ className, reportNode: templateElement }))
    }
    for (const expr of node.expressions) {
      yield* extractClassNames(expr, true)
    }
    return
  }
  if (node.type === 'BinaryExpression') {
    if (node.operator !== '+') {
      return
    }
    yield* extractClassNames(node.left, true)
    yield* extractClassNames(node.right, true)
    return
  }
  if (textOnly) {
    return
  }
  if (node.type === 'ObjectExpression') {
    for (const prop of node.properties) {
      if (prop.type !== 'Property') {
        continue
      }
      const classNames = utils.getStaticPropertyName(prop)
      if (!classNames) {
        continue
      }
      yield* classNames
        .split(/\s+/)
        .map((className) => ({ className, reportNode: prop.key }))
    }
    return
  }
  if (node.type === 'ArrayExpression') {
    for (const element of node.elements) {
      if (element == null) {
        continue
      }
      if (element.type === 'SpreadElement') {
        continue
      }
      yield* extractClassNames(element)
    }
    return
  }
}

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow specific classes in Vue components',
      url: 'https://eslint.vuejs.org/rules/no-restricted-class.html',
      categories: undefined
    },
    fixable: null,
    schema: {
      type: 'array',
      items: {
        type: 'string'
      }
    },
    messages: {
      forbiddenClass: "'{{class}}' class is not allowed."
    }
  },

  /** @param {RuleContext} context */
  create(context) {
    const forbiddenClasses = new Set(context.options || [])
    const forbiddenClassesRegexps = (context.options || [])
      .filter((cl) => regexp.isRegExp(cl))
      .map((cl) => regexp.toRegExp(cl))

    return utils.defineTemplateBodyVisitor(context, {
      /**
       * @param {VAttribute & { value: VLiteral } } node
       */
      'VAttribute[directive=false][key.name="class"][value!=null]'(node) {
        for (const className of node.value.value.split(/\s+/)) {
          reportForbiddenClass(
            className,
            node,
            context,
            forbiddenClasses,
            forbiddenClassesRegexps
          )
        }
      },

      /** @param {VExpressionContainer} node */
      "VAttribute[directive=true][key.name.name='bind'][key.argument.name='class'] > VExpressionContainer.value"(
        node
      ) {
        if (!node.expression) {
          return
        }

        for (const { className, reportNode } of extractClassNames(
          /** @type {Expression} */ (node.expression)
        )) {
          reportForbiddenClass(
            className,
            reportNode,
            context,
            forbiddenClasses,
            forbiddenClassesRegexps
          )
        }
      }
    })
  }
}


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

const utils = require('../utils')
const casing = require('../utils/casing')
const { isRegExp, toRegExp } = require('../utils/regexp')

/**
 * @typedef {object} OptionParsed
 * @property { (name: string) => boolean } test
 * @property {string|undefined} [message]
 * @property {string|undefined} [suggest]
 */

/**
 * @param {string} str
 * @returns {(str: string) => boolean}
 * @private
 */
function buildMatcher(str) {
  if (isRegExp(str)) {
    const regex = toRegExp(str)
    return (s) => regex.test(s)
  }
  return (s) => s === casing.pascalCase(str) || s === casing.kebabCase(str)
}

/**
 * @param {string|{name: string, message?: string, suggest?: string}} option
 * @returns {OptionParsed}
 * @private
 * */
function parseOption(option) {
  if (typeof option === 'string') {
    const matcher = buildMatcher(option)
    return { test: matcher }
  }
  const parsed = parseOption(option.name)
  parsed.message = option.message
  parsed.suggest = option.suggest
  return parsed
}

/**
 * @param {Property | AssignmentProperty} property
 * @param {string | undefined} suggest
 * @returns {Rule.SuggestionReportDescriptor[]}
 * @private
 * */
function createSuggest(property, suggest) {
  if (!suggest) {
    return []
  }

  return [
    {
      fix(fixer) {
        return fixer.replaceText(property.value, JSON.stringify(suggest))
      },
      messageId: 'suggest',
      data: { suggest }
    }
  ]
}

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow specific component names',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/no-restricted-component-names.html'
    },
    fixable: null,
    hasSuggestions: true,
    schema: {
      type: 'array',
      items: {
        oneOf: [
          { type: 'string' },
          {
            type: 'object',
            properties: {
              name: { type: 'string' },
              message: { type: 'string', minLength: 1 },
              suggest: { type: 'string' }
            },
            required: ['name'],
            additionalProperties: false
          }
        ]
      },
      uniqueItems: true,
      minItems: 0
    },
    messages: {
      // eslint-disable-next-line eslint-plugin/report-message-format
      disallow: '{{message}}',
      suggest: 'Instead, change to `{{suggest}}`.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {OptionParsed[]} */
    const options = context.options.map(parseOption)

    /**
     * @param {ObjectExpression} node
     */
    function verify(node) {
      const property = utils.findProperty(node, 'name')
      if (!property) return

      const propertyName = utils.getStaticPropertyName(property)
      if (propertyName === 'name' && property.value.type === 'Literal') {
        const componentName = property.value.value?.toString()
        if (!componentName) {
          return
        }

        for (const option of options) {
          if (option.test(componentName)) {
            context.report({
              node: property.value,
              messageId: 'disallow',
              data: {
                message:
                  option.message ||
                  `Using component name \`${componentName}\` is not allowed.`
              },
              suggest: createSuggest(property, option.suggest)
            })
          }
        }
      }
    }

    return utils.compositingVisitors(
      utils.defineVueVisitor(context, {
        onVueObjectEnter(node) {
          verify(node)
        }
      }),
      utils.defineScriptSetupVisitor(context, {
        onDefineOptionsEnter(node) {
          const expression = node.arguments[0]
          if (expression.type === 'ObjectExpression') {
            verify(expression)
          }
        }
      })
    )
  }
}