Repository: vuejs/eslint-plugin-vue
Pull request #2331 (2023-11-29 10:45:36)

Improve compatibility with ESLint v9
This PR changes to not use APIs scheduled for removal in ESLint v9.

See https://eslint.org/blog/2023/09/preparing-custom-rules-eslint-v9/


Patch:
diff --git a/docs/.vitepress/build-system/shim/eslint/use-at-your-own-risk.mjs b/docs/.vitepress/build-system/shim/eslint/use-at-your-own-risk.mjs
new file mode 100644
index 00000000..0db0b01c
--- /dev/null
+++ b/docs/.vitepress/build-system/shim/eslint/use-at-your-own-risk.mjs
@@ -0,0 +1,3 @@
+export default {
+  /* empty */
+}
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 66e0f3e3..0e1e2173 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -145,6 +145,10 @@ export default async () => {
       plugins: [vitePluginRequireResolve(), viteCommonjs()],
       resolve: {
         alias: {
+          'eslint/use-at-your-own-risk': path.join(
+            dirname,
+            './build-system/shim/eslint/use-at-your-own-risk.mjs'
+          ),
           eslint: path.join(dirname, './build-system/shim/eslint.mjs'),
           assert: path.join(dirname, './build-system/shim/assert.mjs'),
           path: path.join(dirname, './build-system/shim/path.mjs'),
diff --git a/docs/rules/no-deprecated-slot-attribute.md b/docs/rules/no-deprecated-slot-attribute.md
index 59e2c068..afe02f83 100644
--- a/docs/rules/no-deprecated-slot-attribute.md
+++ b/docs/rules/no-deprecated-slot-attribute.md
@@ -37,6 +37,49 @@ This rule reports deprecated `slot` attribute in Vue.js v2.6.0+.
 
 </eslint-code-block>
 
+## :wrench: Options
+
+```json
+{
+  "vue/no-deprecated-slot-attribute": ["error", {
+    "ignore": ["my-component"]
+  }]
+}
+```
+
+- `"ignore"` (`string[]`) An array of tags that ignore this rules. This option will check both kebab-case and PascalCase versions of the given tag names. Default is empty.
+
+### `"ignore": ["my-component"]`
+
+<eslint-code-block :rules="{'vue/no-dupe-keys': ['error', {ignore: ['my-component']}]}">
+
+```vue
+<template>
+  <ListComponent>
+    <!-- ✓ GOOD -->
+    <template v-slot:name>
+      {{ props.title }}
+    </template>
+  </ListComponent>
+
+  <ListComponent>
+    <!-- ✓ GOOD -->
+    <my-component slot="name">
+      {{ props.title }}
+    </my-component>
+  </ListComponent>
+
+    <ListComponent>
+    <!-- ✗ BAD -->
+    <other-component slot="name">
+      {{ props.title }}
+    </other-component>
+  </ListComponent>
+</template>
+```
+
+</eslint-code-block>
+
 ## :books: Further Reading
 
 - [API - slot](https://v2.vuejs.org/v2/api/#slot-deprecated)
diff --git a/eslint.config.js b/eslint.config.js
index abedce70..3407dd3b 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -160,6 +160,20 @@ module.exports = [
       'dot-notation': 'error',
       'arrow-body-style': 'error',
 
+      'no-restricted-properties': [
+        'error',
+        {
+          object: 'context',
+          property: 'parserServices',
+          message: 'Use sourceCode.parserServices'
+        },
+        {
+          object: 'context',
+          property: 'getScope',
+          message: 'Use utils.getScope'
+        }
+      ],
+
       'unicorn/consistent-function-scoping': [
         'error',
         { checkArrowFunctions: false }
diff --git a/lib/rules/block-order.js b/lib/rules/block-order.js
index 91d3eaa6..c93de09b 100644
--- a/lib/rules/block-order.js
+++ b/lib/rules/block-order.js
@@ -106,9 +106,10 @@ module.exports = {
     function getOrderElement(element) {
       return orders.find((o) => o.selector.test(element))
     }
+    const sourceCode = context.getSourceCode()
     const documentFragment =
-      context.parserServices.getDocumentFragment &&
-      context.parserServices.getDocumentFragment()
+      sourceCode.parserServices.getDocumentFragment &&
+      sourceCode.parserServices.getDocumentFragment()
 
     function getTopLevelHTMLElements() {
       if (documentFragment) {
diff --git a/lib/rules/block-tag-newline.js b/lib/rules/block-tag-newline.js
index bccecd83..22fb1287 100644
--- a/lib/rules/block-tag-newline.js
+++ b/lib/rules/block-tag-newline.js
@@ -84,15 +84,14 @@ module.exports = {
   },
   /** @param {RuleContext} context */
   create(context) {
+    const sourceCode = context.getSourceCode()
     const df =
-      context.parserServices.getDocumentFragment &&
-      context.parserServices.getDocumentFragment()
+      sourceCode.parserServices.getDocumentFragment &&
+      sourceCode.parserServices.getDocumentFragment()
     if (!df) {
       return {}
     }
 
-    const sourceCode = context.getSourceCode()
-
     /**
      * @param {VStartTag} startTag
      * @param {string} beforeText
diff --git a/lib/rules/comment-directive.js b/lib/rules/comment-directive.js
index aeca98ae..655e222b 100644
--- a/lib/rules/comment-directive.js
+++ b/lib/rules/comment-directive.js
@@ -313,9 +313,10 @@ module.exports = {
     const options = context.options[0] || {}
     /** @type {boolean} */
     const reportUnusedDisableDirectives = options.reportUnusedDisableDirectives
+    const sourceCode = context.getSourceCode()
     const documentFragment =
-      context.parserServices.getDocumentFragment &&
-      context.parserServices.getDocumentFragment()
+      sourceCode.parserServices.getDocumentFragment &&
+      sourceCode.parserServices.getDocumentFragment()
 
     return {
       Program(node) {
diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js
index 487af771..629f5120 100644
--- a/lib/rules/component-name-in-template-casing.js
+++ b/lib/rules/component-name-in-template-casing.js
@@ -86,9 +86,10 @@ module.exports = {
     /** @type {string[]} */
     const globals = (options.globals || []).map(casing.pascalCase)
     const registeredComponentsOnly = options.registeredComponentsOnly !== false
+    const sourceCode = context.getSourceCode()
     const tokens =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     /** @type { Set<string> } */
     const registeredComponents = new Set(globals)
diff --git a/lib/rules/custom-event-name-casing.js b/lib/rules/custom-event-name-casing.js
index 2361badb..09cc2fc3 100644
--- a/lib/rules/custom-event-name-casing.js
+++ b/lib/rules/custom-event-name-casing.js
@@ -210,7 +210,10 @@ module.exports = {
               return
             }
             // const emit = defineEmits()
-            const variable = findVariable(context.getScope(), emitParam)
+            const variable = findVariable(
+              utils.getScope(context, emitParam),
+              emitParam
+            )
             if (!variable) {
               return
             }
@@ -251,7 +254,10 @@ module.exports = {
               }
               const emitParam = emitProperty.value
               // `setup(props, {emit})`
-              const variable = findVariable(context.getScope(), emitParam)
+              const variable = findVariable(
+                utils.getScope(context, emitParam),
+                emitParam
+              )
               if (!variable) {
                 return
               }
@@ -260,7 +266,10 @@ module.exports = {
               }
             } else {
               // `setup(props, context)`
-              const variable = findVariable(context.getScope(), contextParam)
+              const variable = findVariable(
+                utils.getScope(context, contextParam),
+                contextParam
+              )
               if (!variable) {
                 return
               }
diff --git a/lib/rules/first-attribute-linebreak.js b/lib/rules/first-attribute-linebreak.js
index bdaf078c..c023762d 100644
--- a/lib/rules/first-attribute-linebreak.js
+++ b/lib/rules/first-attribute-linebreak.js
@@ -39,9 +39,10 @@ module.exports = {
     const multiline =
       (context.options[0] && context.options[0].multiline) || 'below'
 
+    const sourceCode = context.getSourceCode()
     const template =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     /**
      * Report attribute
diff --git a/lib/rules/html-closing-bracket-newline.js b/lib/rules/html-closing-bracket-newline.js
index e1a746e4..c353c35d 100644
--- a/lib/rules/html-closing-bracket-newline.js
+++ b/lib/rules/html-closing-bracket-newline.js
@@ -59,9 +59,10 @@ module.exports = {
       },
       context.options[0] || {}
     )
+    const sourceCode = context.getSourceCode()
     const template =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     return utils.defineDocumentVisitor(context, {
       /** @param {VStartTag | VEndTag} node */
diff --git a/lib/rules/html-closing-bracket-spacing.js b/lib/rules/html-closing-bracket-spacing.js
index 0d5d477b..daf42d54 100644
--- a/lib/rules/html-closing-bracket-spacing.js
+++ b/lib/rules/html-closing-bracket-spacing.js
@@ -80,8 +80,8 @@ module.exports = {
   create(context) {
     const sourceCode = context.getSourceCode()
     const tokens =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
     const options = parseOptions(context.options[0], tokens)
 
     return utils.defineDocumentVisitor(context, {
diff --git a/lib/rules/html-indent.js b/lib/rules/html-indent.js
index 1670a330..6a423ecc 100644
--- a/lib/rules/html-indent.js
+++ b/lib/rules/html-indent.js
@@ -11,9 +11,10 @@ const utils = require('../utils')
 module.exports = {
   /** @param {RuleContext} context */
   create(context) {
+    const sourceCode = context.getSourceCode()
     const tokenStore =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
     const visitor = indentCommon.defineVisitor(context, tokenStore, {
       baseIndent: 1
     })
diff --git a/lib/rules/html-self-closing.js b/lib/rules/html-self-closing.js
index 621cde6d..9e626f41 100644
--- a/lib/rules/html-self-closing.js
+++ b/lib/rules/html-self-closing.js
@@ -159,7 +159,7 @@ module.exports = {
               },
               fix(fixer) {
                 const tokens =
-                  context.parserServices.getTemplateBodyTokenStore()
+                  sourceCode.parserServices.getTemplateBodyTokenStore()
                 const close = tokens.getLastToken(node.startTag)
                 if (close.type !== 'HTMLTagClose') {
                   return null
@@ -183,7 +183,7 @@ module.exports = {
               },
               fix(fixer) {
                 const tokens =
-                  context.parserServices.getTemplateBodyTokenStore()
+                  sourceCode.parserServices.getTemplateBodyTokenStore()
                 const close = tokens.getLastToken(node.startTag)
                 if (close.type !== 'HTMLSelfClosingTagClose') {
                   return null
diff --git a/lib/rules/max-attributes-per-line.js b/lib/rules/max-attributes-per-line.js
index b1665c17..443826fe 100644
--- a/lib/rules/max-attributes-per-line.js
+++ b/lib/rules/max-attributes-per-line.js
@@ -124,8 +124,8 @@ module.exports = {
     const multilineMaximum = configuration.multiline
     const singlelinemMaximum = configuration.singleline
     const template =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     return utils.defineTemplateBodyVisitor(context, {
       VStartTag(node) {
diff --git a/lib/rules/max-len.js b/lib/rules/max-len.js
index 588f2de7..9347ea78 100644
--- a/lib/rules/max-len.js
+++ b/lib/rules/max-len.js
@@ -307,8 +307,8 @@ module.exports = {
       const scriptTokens = sourceCode.ast.tokens
       const scriptComments = sourceCode.getAllComments()
 
-      if (context.parserServices.getTemplateBodyTokenStore && templateBody) {
-        const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+      if (sourceCode.parserServices.getTemplateBodyTokenStore && templateBody) {
+        const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
 
         const templateTokens = tokenStore.getTokens(templateBody, {
           includeComments: true
diff --git a/lib/rules/max-lines-per-block.js b/lib/rules/max-lines-per-block.js
index bcb82d56..c778ef40 100644
--- a/lib/rules/max-lines-per-block.js
+++ b/lib/rules/max-lines-per-block.js
@@ -65,9 +65,10 @@ module.exports = {
     }
 
     const code = context.getSourceCode()
+    const sourceCode = context.getSourceCode()
     const documentFragment =
-      context.parserServices.getDocumentFragment &&
-      context.parserServices.getDocumentFragment()
+      sourceCode.parserServices.getDocumentFragment &&
+      sourceCode.parserServices.getDocumentFragment()
 
     function getTopLevelHTMLElements() {
       if (documentFragment) {
diff --git a/lib/rules/multiline-html-element-content-newline.js b/lib/rules/multiline-html-element-content-newline.js
index dc411186..bfe0c79f 100644
--- a/lib/rules/multiline-html-element-content-newline.js
+++ b/lib/rules/multiline-html-element-content-newline.js
@@ -98,10 +98,10 @@ module.exports = {
     const ignores = options.ignores
     const ignoreWhenEmpty = options.ignoreWhenEmpty
     const allowEmptyLines = options.allowEmptyLines
-    const template =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
     const sourceCode = context.getSourceCode()
+    const template =
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     /** @type {VElement | null} */
     let inIgnoreElement = null
diff --git a/lib/rules/mustache-interpolation-spacing.js b/lib/rules/mustache-interpolation-spacing.js
index 51b19c61..79bae054 100644
--- a/lib/rules/mustache-interpolation-spacing.js
+++ b/lib/rules/mustache-interpolation-spacing.js
@@ -30,9 +30,10 @@ module.exports = {
   /** @param {RuleContext} context */
   create(context) {
     const options = context.options[0] || 'always'
+    const sourceCode = context.getSourceCode()
     const template =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     return utils.defineTemplateBodyVisitor(context, {
       /** @param {VExpressionContainer} node */
diff --git a/lib/rules/next-tick-style.js b/lib/rules/next-tick-style.js
index 34e0c3db..08cc85a1 100644
--- a/lib/rules/next-tick-style.js
+++ b/lib/rules/next-tick-style.js
@@ -43,7 +43,10 @@ function getVueNextTickCallExpression(identifier, context) {
     identifier.parent.type === 'CallExpression' &&
     identifier.parent.callee === identifier
   ) {
-    const variable = findVariable(context.getScope(), identifier)
+    const variable = findVariable(
+      utils.getScope(context, identifier),
+      identifier
+    )
 
     if (variable != null && variable.defs.length === 1) {
       const def = variable.defs[0]
diff --git a/lib/rules/no-async-in-computed-properties.js b/lib/rules/no-async-in-computed-properties.js
index 2df1e55c..6ee4373f 100644
--- a/lib/rules/no-async-in-computed-properties.js
+++ b/lib/rules/no-async-in-computed-properties.js
@@ -260,8 +260,9 @@ module.exports = {
 
     return utils.compositingVisitors(
       {
-        Program() {
-          const tracker = new ReferenceTracker(context.getScope())
+        /** @param {Program} program */
+        Program(program) {
+          const tracker = new ReferenceTracker(utils.getScope(context, program))
           const traceMap = utils.createCompositionApiTraceMap({
             [ReferenceTracker.ESM]: true,
             computed: {
diff --git a/lib/rules/no-child-content.js b/lib/rules/no-child-content.js
index 6ccf10d1..62de865b 100644
--- a/lib/rules/no-child-content.js
+++ b/lib/rules/no-child-content.js
@@ -123,8 +123,8 @@ module.exports = {
         if (elementNode.endTag === null) {
           return
         }
-
-        const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+        const sourceCode = context.getSourceCode()
+        const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
         const elementComments = tokenStore.getTokensBetween(
           elementNode.startTag,
           elementNode.endTag,
diff --git a/lib/rules/no-deprecated-slot-attribute.js b/lib/rules/no-deprecated-slot-attribute.js
index 6459cdff..f4c89353 100644
--- a/lib/rules/no-deprecated-slot-attribute.js
+++ b/lib/rules/no-deprecated-slot-attribute.js
@@ -16,7 +16,19 @@ module.exports = {
       url: 'https://eslint.vuejs.org/rules/no-deprecated-slot-attribute.html'
     },
     fixable: 'code',
-    schema: [],
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          ignore: {
+            type: 'array',
+            items: { type: 'string' },
+            uniqueItems: true
+          }
+        },
+        additionalProperties: false
+      }
+    ],
     messages: {
       forbiddenSlotAttribute: '`slot` attributes are deprecated.'
     }
diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js
index c690d8aa..dd8b51c6 100644
--- a/lib/rules/no-dupe-keys.js
+++ b/lib/rules/no-dupe-keys.js
@@ -118,7 +118,10 @@ module.exports = {
           for (const prop of props) {
             if (!prop.propName) continue
 
-            const variable = findVariable(context.getScope(), prop.propName)
+            const variable = findVariable(
+              utils.getScope(context, node),
+              prop.propName
+            )
             if (!variable || variable.defs.length === 0) continue
 
             if (
@@ -149,7 +152,7 @@ module.exports = {
      */
     function extractReferences(node) {
       if (node.type === 'Identifier') {
-        const variable = findVariable(context.getScope(), node)
+        const variable = findVariable(utils.getScope(context, node), node)
         if (!variable) {
           return []
         }
diff --git a/lib/rules/no-dupe-v-else-if.js b/lib/rules/no-dupe-v-else-if.js
index 5102f6dc..dc7c18c0 100644
--- a/lib/rules/no-dupe-v-else-if.js
+++ b/lib/rules/no-dupe-v-else-if.js
@@ -83,9 +83,10 @@ module.exports = {
   },
   /** @param {RuleContext} context */
   create(context) {
+    const sourceCode = context.getSourceCode()
     const tokenStore =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
     /**
      * Determines whether the two given nodes are considered to be equal. In particular, given that the nodes
      * represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators.
diff --git a/lib/rules/no-empty-component-block.js b/lib/rules/no-empty-component-block.js
index 8a9420fc..b2e428bc 100644
--- a/lib/rules/no-empty-component-block.js
+++ b/lib/rules/no-empty-component-block.js
@@ -57,10 +57,11 @@ module.exports = {
    * @returns {RuleListener} AST event handlers.
    */
   create(context) {
-    if (!context.parserServices.getDocumentFragment) {
+    const sourceCode = context.getSourceCode()
+    if (!sourceCode.parserServices.getDocumentFragment) {
       return {}
     }
-    const documentFragment = context.parserServices.getDocumentFragment()
+    const documentFragment = sourceCode.parserServices.getDocumentFragment()
     if (!documentFragment) {
       return {}
     }
diff --git a/lib/rules/no-expose-after-await.js b/lib/rules/no-expose-after-await.js
index 64661f1f..e5ede644 100644
--- a/lib/rules/no-expose-after-await.js
+++ b/lib/rules/no-expose-after-await.js
@@ -192,7 +192,10 @@ module.exports = {
             // `setup(props, {emit})`
             const variable =
               exposeParam.type === 'Identifier'
-                ? findVariable(context.getScope(), exposeParam)
+                ? findVariable(
+                    utils.getScope(context, exposeParam),
+                    exposeParam
+                  )
                 : null
             if (!variable) {
               return
@@ -205,7 +208,10 @@ module.exports = {
             }
           } else if (contextParam.type === 'Identifier') {
             // `setup(props, context)`
-            const variable = findVariable(context.getScope(), contextParam)
+            const variable = findVariable(
+              utils.getScope(context, contextParam),
+              contextParam
+            )
             if (!variable) {
               return
             }
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 7264677c..afd0e061 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -84,10 +84,11 @@ function isIIFE(node) {
  * @returns {TemplateListener} AST event handlers.
  */
 function createForVueSyntax(context) {
-  if (!context.parserServices.getTemplateBodyTokenStore) {
+  const sourceCode = context.getSourceCode()
+  if (!sourceCode.parserServices.getTemplateBodyTokenStore) {
     return {}
   }
-  const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+  const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
 
   /**
    * Checks if the given node turns into a filter when unwraped.
diff --git a/lib/rules/no-lifecycle-after-await.js b/lib/rules/no-lifecycle-after-await.js
index 6a54f1c5..21416f89 100644
--- a/lib/rules/no-lifecycle-after-await.js
+++ b/lib/rules/no-lifecycle-after-await.js
@@ -60,8 +60,9 @@ module.exports = {
 
     return utils.compositingVisitors(
       {
-        Program() {
-          const tracker = new ReferenceTracker(context.getScope())
+        /** @param {Program} program */
+        Program(program) {
+          const tracker = new ReferenceTracker(utils.getScope(context, program))
           const traceMap = {
             /** @type {TraceMap} */
             vue: {
diff --git a/lib/rules/no-multi-spaces.js b/lib/rules/no-multi-spaces.js
index 1dbac86e..7e8f192a 100644
--- a/lib/rules/no-multi-spaces.js
+++ b/lib/rules/no-multi-spaces.js
@@ -52,7 +52,8 @@ module.exports = {
 
     return {
       Program(node) {
-        if (context.parserServices.getTemplateBodyTokenStore == null) {
+        const sourceCode = context.getSourceCode()
+        if (sourceCode.parserServices.getTemplateBodyTokenStore == null) {
           const filename = context.getFilename()
           if (path.extname(filename) === '.vue') {
             context.report({
@@ -65,8 +66,7 @@ module.exports = {
         if (!node.templateBody) {
           return
         }
-        const sourceCode = context.getSourceCode()
-        const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+        const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
         const tokens = tokenStore.getTokens(node.templateBody, {
           includeComments: true
         })
diff --git a/lib/rules/no-multiple-slot-args.js b/lib/rules/no-multiple-slot-args.js
index 1b57832a..86bd6303 100644
--- a/lib/rules/no-multiple-slot-args.js
+++ b/lib/rules/no-multiple-slot-args.js
@@ -75,7 +75,7 @@ module.exports = {
      * @param {Identifier} node The node to verify
      */
     function verifyReferences(node) {
-      const variable = findVariable(context.getScope(), node)
+      const variable = findVariable(utils.getScope(context, node), node)
       if (!variable) {
         return
       }
diff --git a/lib/rules/no-mutating-props.js b/lib/rules/no-mutating-props.js
index 57a19199..c00a9224 100644
--- a/lib/rules/no-mutating-props.js
+++ b/lib/rules/no-mutating-props.js
@@ -224,7 +224,7 @@ module.exports = {
      * @param {string[]} path
      */
     function verifyPropVariable(prop, path) {
-      const variable = findVariable(context.getScope(), prop)
+      const variable = findVariable(utils.getScope(context, prop), prop)
       if (!variable) {
         return
       }
diff --git a/lib/rules/no-restricted-block.js b/lib/rules/no-restricted-block.js
index 8fc2b51b..87b4bda3 100644
--- a/lib/rules/no-restricted-block.js
+++ b/lib/rules/no-restricted-block.js
@@ -90,9 +90,10 @@ module.exports = {
     /** @type {ParsedOption[]} */
     const options = context.options.map(parseOption)
 
+    const sourceCode = context.getSourceCode()
     const documentFragment =
-      context.parserServices.getDocumentFragment &&
-      context.parserServices.getDocumentFragment()
+      sourceCode.parserServices.getDocumentFragment &&
+      sourceCode.parserServices.getDocumentFragment()
 
     function getTopLevelHTMLElements() {
       if (documentFragment) {
diff --git a/lib/rules/no-restricted-call-after-await.js b/lib/rules/no-restricted-call-after-await.js
index d2b2a141..b235896a 100644
--- a/lib/rules/no-restricted-call-after-await.js
+++ b/lib/rules/no-restricted-call-after-await.js
@@ -144,7 +144,7 @@ module.exports = {
       {
         /** @param {Program} node */
         Program(node) {
-          const tracker = new ReferenceTracker(context.getScope())
+          const tracker = new ReferenceTracker(utils.getScope(context, node))
 
           for (const option of context.options) {
             const modules = normalizeModules(option.module, node)
diff --git a/lib/rules/no-restricted-custom-event.js b/lib/rules/no-restricted-custom-event.js
index f153b692..98a87d80 100644
--- a/lib/rules/no-restricted-custom-event.js
+++ b/lib/rules/no-restricted-custom-event.js
@@ -213,7 +213,10 @@ module.exports = {
               }
               const emitParam = emitProperty.value
               // `setup(props, {emit})`
-              const variable = findVariable(context.getScope(), emitParam)
+              const variable = findVariable(
+                utils.getScope(context, emitParam),
+                emitParam
+              )
               if (!variable) {
                 return
               }
@@ -222,7 +225,10 @@ module.exports = {
               }
             } else {
               // `setup(props, context)`
-              const variable = findVariable(context.getScope(), contextParam)
+              const variable = findVariable(
+                utils.getScope(context, contextParam),
+                contextParam
+              )
               if (!variable) {
                 return
               }
diff --git a/lib/rules/no-setup-props-reactivity-loss.js b/lib/rules/no-setup-props-reactivity-loss.js
index 1292ace7..fc0406ba 100644
--- a/lib/rules/no-setup-props-reactivity-loss.js
+++ b/lib/rules/no-setup-props-reactivity-loss.js
@@ -123,9 +123,10 @@ module.exports = {
     /**
      * @param {Pattern | null} node
      * @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program} scopeNode
+     * @param {import('eslint').Scope.Scope} currentScope
      * @param {string} scopeName
      */
-    function processPattern(node, scopeNode, scopeName) {
+    function processPattern(node, scopeNode, currentScope, scopeName) {
       if (!node) {
         // no arguments
         return
@@ -143,14 +144,14 @@ module.exports = {
         return
       }
 
-      const variable = findVariable(context.getScope(), node)
+      const variable = findVariable(currentScope, node)
       if (!variable) {
         return
       }
       const propsReferenceIds = new Set()
       for (const reference of variable.references) {
         // If reference is in another scope, we can't check it.
-        if (reference.from !== context.getScope()) {
+        if (reference.from !== currentScope) {
           continue
         }
 
@@ -258,13 +259,20 @@ module.exports = {
           } else if (target.parent.type === 'AssignmentExpression') {
             id = target.parent.right === target ? target.parent.left : null
           }
-          processPattern(id, context.getSourceCode().ast, '<script setup>')
+          const currentScope = utils.getScope(context, node)
+          processPattern(
+            id,
+            context.getSourceCode().ast,
+            currentScope,
+            '<script setup>'
+          )
         }
       }),
       utils.defineVueVisitor(context, {
         onSetupFunctionEnter(node) {
+          const currentScope = utils.getScope(context, node)
           const propsParam = utils.skipDefaultParamValue(node.params[0])
-          processPattern(propsParam, node, 'setup()')
+          processPattern(propsParam, node, currentScope, 'setup()')
         }
       })
     )
diff --git a/lib/rules/no-side-effects-in-computed-properties.js b/lib/rules/no-side-effects-in-computed-properties.js
index 903cf43e..76127c37 100644
--- a/lib/rules/no-side-effects-in-computed-properties.js
+++ b/lib/rules/no-side-effects-in-computed-properties.js
@@ -135,7 +135,7 @@ module.exports = {
           return
         }
 
-        const variable = findVariable(context.getScope(), node)
+        const variable = findVariable(utils.getScope(context, node), node)
         if (!variable || variable.defs.length !== 1) {
           return
         }
@@ -180,8 +180,9 @@ module.exports = {
     }
     return utils.compositingVisitors(
       {
-        Program() {
-          const tracker = new ReferenceTracker(context.getScope())
+        /** @param {Program} program */
+        Program(program) {
+          const tracker = new ReferenceTracker(utils.getScope(context, program))
           const traceMap = utils.createCompositionApiTraceMap({
             [ReferenceTracker.ESM]: true,
             computed: {
diff --git a/lib/rules/no-unused-emit-declarations.js b/lib/rules/no-unused-emit-declarations.js
index 1414b50c..f96004a3 100644
--- a/lib/rules/no-unused-emit-declarations.js
+++ b/lib/rules/no-unused-emit-declarations.js
@@ -253,7 +253,7 @@ module.exports = {
             // `setup(props, {emit})`
             const variable =
               emitParam.type === 'Identifier'
-                ? findVariable(context.getScope(), emitParam)
+                ? findVariable(utils.getScope(context, emitParam), emitParam)
                 : null
             if (!variable) {
               return
@@ -263,7 +263,10 @@ module.exports = {
             }
           } else if (contextParam.type === 'Identifier') {
             // `setup(props, context)`
-            const variable = findVariable(context.getScope(), contextParam)
+            const variable = findVariable(
+              utils.getScope(context, contextParam),
+              contextParam
+            )
             for (const reference of variable.references) {
               contextReferenceIds.add(reference.identifier)
             }
@@ -294,7 +297,7 @@ module.exports = {
           const emitParam = node.parent.id
           const variable =
             emitParam.type === 'Identifier'
-              ? findVariable(context.getScope(), emitParam)
+              ? findVariable(utils.getScope(context, emitParam), emitParam)
               : null
           if (!variable) {
             return
diff --git a/lib/rules/no-useless-mustaches.js b/lib/rules/no-useless-mustaches.js
index 884cdf7a..f4f858d9 100644
--- a/lib/rules/no-useless-mustaches.js
+++ b/lib/rules/no-useless-mustaches.js
@@ -92,8 +92,7 @@ module.exports = {
       } else {
         return
       }
-
-      const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+      const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
       const hasComment = tokenStore
         .getTokens(node, { includeComments: true })
         .some((t) => t.type === 'Block' || t.type === 'Line')
diff --git a/lib/rules/no-useless-v-bind.js b/lib/rules/no-useless-v-bind.js
index c78cd6b4..34f0ad03 100644
--- a/lib/rules/no-useless-v-bind.js
+++ b/lib/rules/no-useless-v-bind.js
@@ -76,7 +76,7 @@ module.exports = {
         return
       }
 
-      const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+      const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
       const hasComment = tokenStore
         .getTokens(value, { includeComments: true })
         .some((t) => t.type === 'Block' || t.type === 'Line')
diff --git a/lib/rules/no-watch-after-await.js b/lib/rules/no-watch-after-await.js
index f566fb24..cbe1772b 100644
--- a/lib/rules/no-watch-after-await.js
+++ b/lib/rules/no-watch-after-await.js
@@ -75,8 +75,9 @@ module.exports = {
 
     return utils.compositingVisitors(
       {
-        Program() {
-          const tracker = new ReferenceTracker(context.getScope())
+        /** @param {Program} program */
+        Program(program) {
+          const tracker = new ReferenceTracker(utils.getScope(context, program))
           const traceMap = {
             vue: {
               [ReferenceTracker.ESM]: true,
diff --git a/lib/rules/padding-line-between-blocks.js b/lib/rules/padding-line-between-blocks.js
index e962488d..f0765572 100644
--- a/lib/rules/padding-line-between-blocks.js
+++ b/lib/rules/padding-line-between-blocks.js
@@ -132,10 +132,11 @@ module.exports = {
   },
   /** @param {RuleContext} context */
   create(context) {
-    if (!context.parserServices.getDocumentFragment) {
+    const sourceCode = context.getSourceCode()
+    if (!sourceCode.parserServices.getDocumentFragment) {
       return {}
     }
-    const df = context.parserServices.getDocumentFragment()
+    const df = sourceCode.parserServices.getDocumentFragment()
     if (!df) {
       return {}
     }
diff --git a/lib/rules/prefer-separate-static-class.js b/lib/rules/prefer-separate-static-class.js
index 7adc68cb..f01de122 100644
--- a/lib/rules/prefer-separate-static-class.js
+++ b/lib/rules/prefer-separate-static-class.js
@@ -169,8 +169,9 @@ module.exports = {
                     return
                   }
 
+                  const sourceCode = context.getSourceCode()
                   const tokenStore =
-                    context.parserServices.getTemplateBodyTokenStore()
+                    sourceCode.parserServices.getTemplateBodyTokenStore()
 
                   if (elements.length === 1) {
                     yield* removeNodeWithComma(fixer, tokenStore, listNode)
diff --git a/lib/rules/require-explicit-emits.js b/lib/rules/require-explicit-emits.js
index 8b9645f5..7e06bf47 100644
--- a/lib/rules/require-explicit-emits.js
+++ b/lib/rules/require-explicit-emits.js
@@ -285,7 +285,7 @@ module.exports = {
             const emitParam = node.parent.id
             const variable =
               emitParam.type === 'Identifier'
-                ? findVariable(context.getScope(), emitParam)
+                ? findVariable(utils.getScope(context, emitParam), emitParam)
                 : null
             if (!variable) {
               return
@@ -361,7 +361,7 @@ module.exports = {
               // `setup(props, {emit})`
               const variable =
                 emitParam.type === 'Identifier'
-                  ? findVariable(context.getScope(), emitParam)
+                  ? findVariable(utils.getScope(context, emitParam), emitParam)
                   : null
               if (!variable) {
                 return
@@ -375,7 +375,10 @@ module.exports = {
               }
             } else if (contextParam.type === 'Identifier') {
               // `setup(props, context)`
-              const variable = findVariable(context.getScope(), contextParam)
+              const variable = findVariable(
+                utils.getScope(context, contextParam),
+                contextParam
+              )
               if (!variable) {
                 return
               }
diff --git a/lib/rules/require-expose.js b/lib/rules/require-expose.js
index c7e4e112..41578839 100644
--- a/lib/rules/require-expose.js
+++ b/lib/rules/require-expose.js
@@ -121,7 +121,7 @@ module.exports = {
         return true
       }
       if (node.type === 'Identifier') {
-        const variable = findVariable(context.getScope(), node)
+        const variable = findVariable(utils.getScope(context, node), node)
         if (variable) {
           for (const def of variable.defs) {
             if (def.type === 'FunctionName') {
@@ -167,7 +167,7 @@ module.exports = {
           // `setup(props, {emit})`
           const variable =
             exposeParam.type === 'Identifier'
-              ? findVariable(context.getScope(), exposeParam)
+              ? findVariable(utils.getScope(context, exposeParam), exposeParam)
               : null
           if (!variable) {
             return
@@ -180,7 +180,10 @@ module.exports = {
           }
         } else if (contextParam.type === 'Identifier') {
           // `setup(props, context)`
-          const variable = findVariable(context.getScope(), contextParam)
+          const variable = findVariable(
+            utils.getScope(context, contextParam),
+            contextParam
+          )
           if (!variable) {
             return
           }
diff --git a/lib/rules/require-slots-as-functions.js b/lib/rules/require-slots-as-functions.js
index 671623cb..686677ea 100644
--- a/lib/rules/require-slots-as-functions.js
+++ b/lib/rules/require-slots-as-functions.js
@@ -78,7 +78,7 @@ module.exports = {
      * @param {Expression} reportNode The node to report
      */
     function verifyReferences(node, reportNode) {
-      const variable = findVariable(context.getScope(), node)
+      const variable = findVariable(utils.getScope(context, node), node)
       if (!variable) {
         return
       }
diff --git a/lib/rules/require-typed-ref.js b/lib/rules/require-typed-ref.js
index e26ac133..60a82432 100644
--- a/lib/rules/require-typed-ref.js
+++ b/lib/rules/require-typed-ref.js
@@ -53,7 +53,11 @@ module.exports = {
       return {}
     }
 
-    const defines = iterateDefineRefs(context.getScope())
+    const defines = iterateDefineRefs(
+      /** @type {import('eslint').Scope.Scope} */ (
+        context.getSourceCode().scopeManager.globalScope
+      )
+    )
 
     /**
      * @param {string} name
diff --git a/lib/rules/return-in-computed-property.js b/lib/rules/return-in-computed-property.js
index 41860eba..54d2553a 100644
--- a/lib/rules/return-in-computed-property.js
+++ b/lib/rules/return-in-computed-property.js
@@ -54,8 +54,9 @@ module.exports = {
 
     return Object.assign(
       {
-        Program() {
-          const tracker = new ReferenceTracker(context.getScope())
+        /** @param {Program} program */
+        Program(program) {
+          const tracker = new ReferenceTracker(utils.getScope(context, program))
           const traceMap = utils.createCompositionApiTraceMap({
             [ReferenceTracker.ESM]: true,
             computed: {
diff --git a/lib/rules/singleline-html-element-content-newline.js b/lib/rules/singleline-html-element-content-newline.js
index d89fd940..69d50734 100644
--- a/lib/rules/singleline-html-element-content-newline.js
+++ b/lib/rules/singleline-html-element-content-newline.js
@@ -92,10 +92,10 @@ module.exports = {
     const ignores = new Set([...options.ignores, ...options.externalIgnores])
     const ignoreWhenNoAttributes = options.ignoreWhenNoAttributes
     const ignoreWhenEmpty = options.ignoreWhenEmpty
-    const template =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
     const sourceCode = context.getSourceCode()
+    const template =
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     /** @type {VElement | null} */
     let inIgnoreElement = null
diff --git a/lib/rules/syntaxes/slot-attribute.js b/lib/rules/syntaxes/slot-attribute.js
index 3292cd9d..a9699817 100644
--- a/lib/rules/syntaxes/slot-attribute.js
+++ b/lib/rules/syntaxes/slot-attribute.js
@@ -5,16 +5,21 @@
 'use strict'
 
 const canConvertToVSlot = require('./utils/can-convert-to-v-slot')
+const casing = require('../../utils/casing')
 
 module.exports = {
   deprecated: '2.6.0',
   supported: '<3.0.0',
   /** @param {RuleContext} context @returns {TemplateListener} */
   createTemplateBodyVisitor(context) {
+    const options = context.options[0] || {}
+    /** @type {Set<string>} */
+    const ignore = new Set(options.ignore)
+
     const sourceCode = context.getSourceCode()
     const tokenStore =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     /**
      * Checks whether the given node can convert to the `v-slot`.
@@ -95,6 +100,15 @@ module.exports = {
      * @returns {void}
      */
     function reportSlot(slotAttr) {
+      const componentName = slotAttr.parent.parent.rawName
+      if (
+        ignore.has(componentName) ||
+        ignore.has(casing.pascalCase(componentName)) ||
+        ignore.has(casing.kebabCase(componentName))
+      ) {
+        return
+      }
+
       context.report({
         node: slotAttr.key,
         messageId: 'forbiddenSlotAttribute',
diff --git a/lib/rules/syntaxes/slot-scope-attribute.js b/lib/rules/syntaxes/slot-scope-attribute.js
index 53a54d6d..7332c5b7 100644
--- a/lib/rules/syntaxes/slot-scope-attribute.js
+++ b/lib/rules/syntaxes/slot-scope-attribute.js
@@ -18,8 +18,8 @@ module.exports = {
   createTemplateBodyVisitor(context, { fixToUpgrade } = {}) {
     const sourceCode = context.getSourceCode()
     const tokenStore =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
 
     /**
      * Checks whether the given node can convert to the `v-slot`.
diff --git a/lib/rules/v-for-delimiter-style.js b/lib/rules/v-for-delimiter-style.js
index c0e3df65..3a494480 100644
--- a/lib/rules/v-for-delimiter-style.js
+++ b/lib/rules/v-for-delimiter-style.js
@@ -32,9 +32,10 @@ module.exports = {
     return utils.defineTemplateBodyVisitor(context, {
       /** @param {VForExpression} node */
       VForExpression(node) {
+        const sourceCode = context.getSourceCode()
         const tokenStore =
-          context.parserServices.getTemplateBodyTokenStore &&
-          context.parserServices.getTemplateBodyTokenStore()
+          sourceCode.parserServices.getTemplateBodyTokenStore &&
+          sourceCode.parserServices.getTemplateBodyTokenStore()
 
         const delimiterToken = /** @type {Token} */ (
           tokenStore.getTokenAfter(
diff --git a/lib/rules/v-on-function-call.js b/lib/rules/v-on-function-call.js
index ba91b92b..efd5b6fa 100644
--- a/lib/rules/v-on-function-call.js
+++ b/lib/rules/v-on-function-call.js
@@ -126,7 +126,9 @@ module.exports = {
             return
           }
 
-          const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+          const sourceCode = context.getSourceCode()
+          const tokenStore =
+            sourceCode.parserServices.getTemplateBodyTokenStore()
           const tokens = tokenStore.getTokens(node.parent, {
             includeComments: true
           })
diff --git a/lib/rules/v-on-handler-style.js b/lib/rules/v-on-handler-style.js
index 89096515..10ff9b6b 100644
--- a/lib/rules/v-on-handler-style.js
+++ b/lib/rules/v-on-handler-style.js
@@ -236,7 +236,8 @@ module.exports = {
      * @param {VExpressionContainer} node
      */
     function getVExpressionContainerTokenInfo(node) {
-      const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+      const sourceCode = context.getSourceCode()
+      const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
       const tokens = tokenStore.getTokens(node, {
         includeComments: true
       })
@@ -432,7 +433,9 @@ module.exports = {
             return
           }
           yield fixer.insertTextBeforeRange(rangeWithoutQuotes, '() => ')
-          const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+          const sourceCode = context.getSourceCode()
+          const tokenStore =
+            sourceCode.parserServices.getTemplateBodyTokenStore()
           const firstToken = tokenStore.getFirstToken(node)
           const lastToken = tokenStore.getLastToken(node)
           if (firstToken.value === '{' && lastToken.value === '}') return
diff --git a/lib/rules/valid-define-emits.js b/lib/rules/valid-define-emits.js
index 2c83faa2..6f6085cb 100644
--- a/lib/rules/valid-define-emits.js
+++ b/lib/rules/valid-define-emits.js
@@ -69,7 +69,7 @@ module.exports = {
         Identifier(node) {
           for (const defineEmits of emitsDefExpressions) {
             if (utils.inRange(defineEmits.range, node)) {
-              const variable = findVariable(context.getScope(), node)
+              const variable = findVariable(utils.getScope(context, node), node)
               if (
                 variable &&
                 variable.references.some((ref) => ref.identifier === node) &&
diff --git a/lib/rules/valid-define-options.js b/lib/rules/valid-define-options.js
index fad893b4..71dff325 100644
--- a/lib/rules/valid-define-options.js
+++ b/lib/rules/valid-define-options.js
@@ -86,7 +86,7 @@ module.exports = {
         Identifier(node) {
           for (const defineOptions of optionsDefExpressions) {
             if (utils.inRange(defineOptions.range, node)) {
-              const variable = findVariable(context.getScope(), node)
+              const variable = findVariable(utils.getScope(context, node), node)
               if (
                 variable &&
                 variable.references.some((ref) => ref.identifier === node) &&
diff --git a/lib/rules/valid-define-props.js b/lib/rules/valid-define-props.js
index 7e3b0a34..bd1e41bd 100644
--- a/lib/rules/valid-define-props.js
+++ b/lib/rules/valid-define-props.js
@@ -70,7 +70,7 @@ module.exports = {
         Identifier(node) {
           for (const defineProps of propsDefExpressions) {
             if (utils.inRange(defineProps.range, node)) {
-              const variable = findVariable(context.getScope(), node)
+              const variable = findVariable(utils.getScope(context, node), node)
               if (
                 variable &&
                 variable.references.some((ref) => ref.identifier === node) &&
diff --git a/lib/rules/valid-next-tick.js b/lib/rules/valid-next-tick.js
index f9743fc1..969488cb 100644
--- a/lib/rules/valid-next-tick.js
+++ b/lib/rules/valid-next-tick.js
@@ -35,7 +35,7 @@ function getVueNextTickNode(identifier, context) {
   }
 
   // Vue 3 Global API: import { nextTick as nt } from 'vue'; nt()
-  const variable = findVariable(context.getScope(), identifier)
+  const variable = findVariable(utils.getScope(context, identifier), identifier)
 
   if (variable != null && variable.defs.length === 1) {
     const def = variable.defs[0]
diff --git a/lib/rules/valid-v-slot.js b/lib/rules/valid-v-slot.js
index c3310796..d1608472 100644
--- a/lib/rules/valid-v-slot.js
+++ b/lib/rules/valid-v-slot.js
@@ -278,8 +278,8 @@ module.exports = {
   create(context) {
     const sourceCode = context.getSourceCode()
     const tokenStore =
-      context.parserServices.getTemplateBodyTokenStore &&
-      context.parserServices.getTemplateBodyTokenStore()
+      sourceCode.parserServices.getTemplateBodyTokenStore &&
+      sourceCode.parserServices.getTemplateBodyTokenStore()
     const options = context.options[0] || {}
     const allowModifiers = options.allowModifiers === true
 
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 4073b691..9bfef357 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -5,6 +5,8 @@
  */
 'use strict'
 
+const { getScope } = require('./scope')
+
 /**
  * @typedef {import('eslint').Rule.RuleModule} RuleModule
  * @typedef {import('estree').Position} Position
@@ -71,7 +73,7 @@ const {
 const componentComments = new WeakMap()
 
 /** @type { Map<string, RuleModule> | null } */
-let ruleMap = null
+let coreRuleMap = null
 /**
  * Get the core rule implementation from the rule name
  * @param {string} name
@@ -79,8 +81,15 @@ let ruleMap = null
  */
 function getCoreRule(name) {
   const eslint = require('eslint')
-  const map = ruleMap || (ruleMap = new eslint.Linter().getRules())
-  return map.get(name) || null
+  try {
+    const map = coreRuleMap || (coreRuleMap = new eslint.Linter().getRules())
+    return map.get(name) || null
+  } catch {
+    // getRules() is no longer available in flat config.
+  }
+
+  const { builtinRules } = require('eslint/use-at-your-own-risk')
+  return /** @type {any} */ (builtinRules.get(name) || null)
 }
 
 /**
@@ -129,8 +138,8 @@ function newProxy(target, ...propsArray) {
 function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
   const eslintSourceCode = context.getSourceCode()
   const rootNode = options.applyDocument
-    ? context.parserServices.getDocumentFragment &&
-      context.parserServices.getDocumentFragment()
+    ? eslintSourceCode.parserServices.getDocumentFragment &&
+      eslintSourceCode.parserServices.getDocumentFragment()
     : eslintSourceCode.ast.templateBody
   /** @type {Token[] | null} */
   let tokensAndComments = null
@@ -427,9 +436,10 @@ module.exports = {
     } = options || {}
     return {
       create(context) {
+        const sourceCode = context.getSourceCode()
         const tokenStore =
-          context.parserServices.getTemplateBodyTokenStore &&
-          context.parserServices.getTemplateBodyTokenStore()
+          sourceCode.parserServices.getTemplateBodyTokenStore &&
+          sourceCode.parserServices.getTemplateBodyTokenStore()
 
         // The `context.getSourceCode()` cannot access the tokens of templates.
         // So override the methods which access to tokens by the `tokenStore`.
@@ -603,9 +613,9 @@ module.exports = {
     if (node.value.expression != null) {
       return false
     }
-
+    const sourceCode = context.getSourceCode()
     const valueNode = node.value
-    const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+    const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
     let quote1 = null
     let quote2 = null
     // `node.value` may be only comments, so cannot get the correct tokens with `tokenStore.getTokens(node.value)`.
@@ -1606,6 +1616,7 @@ module.exports = {
      * @property {boolean} hasReturn
      * @property {boolean} hasReturnValue
      * @property {ArrowFunctionExpression | FunctionExpression | FunctionDeclaration} node
+     * @property {CodePathSegment[]} currentSegments
      */
 
     /** @type {FuncInfo | null} */
@@ -1615,10 +1626,7 @@ module.exports = {
       if (!funcInfo) {
         return true
       }
-      if (
-        funcInfo.codePath &&
-        funcInfo.codePath.currentSegments.some((segment) => segment.reachable)
-      ) {
+      if (funcInfo.currentSegments.some((segment) => segment.reachable)) {
         return false
       }
       return !treatUndefinedAsUnspecified || funcInfo.hasReturnValue
@@ -1637,6 +1645,7 @@ module.exports = {
         ) {
           funcInfo = {
             codePath,
+            currentSegments: [],
             funcInfo,
             hasReturn: false,
             hasReturnValue: false,
@@ -1644,6 +1653,12 @@ module.exports = {
           }
         }
       },
+      onCodePathSegmentStart(segment) {
+        funcInfo?.currentSegments.push(segment)
+      },
+      onCodePathSegmentEnd() {
+        funcInfo?.currentSegments.pop()
+      },
       onCodePathEnd() {
         funcInfo = funcInfo && funcInfo.funcInfo
       },
@@ -1821,7 +1836,7 @@ module.exports = {
       return false
     }
 
-    const variable = findVariable(context.getScope(), node)
+    const variable = findVariable(getScope(context, node), node)
 
     if (variable != null && variable.defs.length === 1) {
       const def = variable.defs[0]
@@ -2050,7 +2065,8 @@ function defineTemplateBodyVisitor(
   scriptVisitor,
   options
 ) {
-  if (context.parserServices.defineTemplateBodyVisitor == null) {
+  const sourceCode = context.getSourceCode()
+  if (sourceCode.parserServices.defineTemplateBodyVisitor == null) {
     const filename = context.getFilename()
     if (path.extname(filename) === '.vue') {
       context.report({
@@ -2061,7 +2077,7 @@ function defineTemplateBodyVisitor(
     }
     return {}
   }
-  return context.parserServices.defineTemplateBodyVisitor(
+  return sourceCode.parserServices.defineTemplateBodyVisitor(
     templateBodyVisitor,
     scriptVisitor,
     options
@@ -2078,7 +2094,8 @@ function defineTemplateBodyVisitor(
  * @returns {RuleListener} The merged visitor.
  */
 function defineDocumentVisitor(context, documentVisitor, options) {
-  if (context.parserServices.defineDocumentVisitor == null) {
+  const sourceCode = context.getSourceCode()
+  if (sourceCode.parserServices.defineDocumentVisitor == null) {
     const filename = context.getFilename()
     if (path.extname(filename) === '.vue') {
       context.report({
@@ -2089,7 +2106,10 @@ function defineDocumentVisitor(context, documentVisitor, options) {
     }
     return {}
   }
-  return context.parserServices.defineDocumentVisitor(documentVisitor, options)
+  return sourceCode.parserServices.defineDocumentVisitor(
+    documentVisitor,
+    options
+  )
 }
 
 /**
@@ -2134,33 +2154,6 @@ function findVariableByIdentifier(context, node) {
   return findVariable(getScope(context, node), node)
 }
 
-/**
- * Gets the scope for the current node
- * @param {RuleContext} context The rule context
- * @param {ESNode} currentNode The node to get the scope of
- * @returns { import('eslint').Scope.Scope } The scope information for this node
- */
-function getScope(context, currentNode) {
-  // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
-  const inner = currentNode.type !== 'Program'
-  const scopeManager = context.getSourceCode().scopeManager
-
-  /** @type {ESNode | null} */
-  let node = currentNode
-  for (; node; node = /** @type {ESNode | null} */ (node.parent)) {
-    const scope = scopeManager.acquire(node, inner)
-
-    if (scope) {
-      if (scope.type === 'function-expression-name') {
-        return scope.childScopes[0]
-      }
-      return scope
-    }
-  }
-
-  return scopeManager.scopes[0]
-}
-
 /**
  * Finds the property with the given name from the given ObjectExpression node.
  * @param {ObjectExpression} node
@@ -2462,9 +2455,10 @@ function isScriptSetup(context) {
  * @returns {VElement | null} the element of `<script setup>`
  */
 function getScriptSetupElement(context) {
+  const sourceCode = context.getSourceCode()
   const df =
-    context.parserServices.getDocumentFragment &&
-    context.parserServices.getDocumentFragment()
+    sourceCode.parserServices.getDocumentFragment &&
+    sourceCode.parserServices.getDocumentFragment()
   if (!df) {
     return null
   }
@@ -2720,7 +2714,7 @@ function isSFCObject(context, node) {
         ) {
           return false
         }
-        const variable = findVariable(context.getScope(), parent.id)
+        const variable = findVariable(getScope(context, node), parent.id)
         if (!variable) {
           return false
         }
@@ -3181,7 +3175,7 @@ function getObjectOrArray(context, node) {
     return node
   }
   if (node.type === 'Identifier') {
-    const variable = findVariable(context.getScope(), node)
+    const variable = findVariable(getScope(context, node), node)
 
     if (variable != null && variable.defs.length === 1) {
       const def = variable.defs[0]
diff --git a/lib/utils/scope.js b/lib/utils/scope.js
new file mode 100644
index 00000000..bc34c642
--- /dev/null
+++ b/lib/utils/scope.js
@@ -0,0 +1,30 @@
+module.exports = {
+  getScope
+}
+
+/**
+ * Gets the scope for the current node
+ * @param {RuleContext} context The rule context
+ * @param {ESNode} currentNode The node to get the scope of
+ * @returns { import('eslint').Scope.Scope } The scope information for this node
+ */
+function getScope(context, currentNode) {
+  // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
+  const inner = currentNode.type !== 'Program'
+  const scopeManager = context.getSourceCode().scopeManager
+
+  /** @type {ESNode | null} */
+  let node = currentNode
+  for (; node; node = /** @type {ESNode | null} */ (node.parent)) {
+    const scope = scopeManager.acquire(node, inner)
+
+    if (scope) {
+      if (scope.type === 'function-expression-name') {
+        return scope.childScopes[0]
+      }
+      return scope
+    }
+  }
+
+  return scopeManager.scopes[0]
+}
diff --git a/lib/utils/style-variables/index.js b/lib/utils/style-variables/index.js
index 841f72f5..66f22430 100644
--- a/lib/utils/style-variables/index.js
+++ b/lib/utils/style-variables/index.js
@@ -40,9 +40,10 @@ const cache = new WeakMap()
  * @returns {StyleVariablesContext | null}
  */
 function getStyleVariablesContext(context) {
+  const sourceCode = context.getSourceCode()
   const df =
-    context.parserServices.getDocumentFragment &&
-    context.parserServices.getDocumentFragment()
+    sourceCode.parserServices.getDocumentFragment &&
+    sourceCode.parserServices.getDocumentFragment()
   if (!df) {
     return null
   }
diff --git a/lib/utils/ts-utils/ts-ast.js b/lib/utils/ts-utils/ts-ast.js
index 3a7356a2..ddbb9de0 100644
--- a/lib/utils/ts-utils/ts-ast.js
+++ b/lib/utils/ts-utils/ts-ast.js
@@ -1,3 +1,4 @@
+const { getScope } = require('../scope')
 const { findVariable } = require('@eslint-community/eslint-utils')
 const { inferRuntimeTypeFromTypeNode } = require('./ts-types')
 /**
@@ -279,7 +280,10 @@ function flattenTypeNodes(context, node) {
       node.typeName.type === 'Identifier'
     ) {
       const refName = node.typeName.name
-      const variable = findVariable(context.getScope(), refName)
+      const variable = findVariable(
+        getScope(context, /** @type {any} */ (node)),
+        refName
+      )
       if (variable && variable.defs.length === 1) {
         const defNode = /** @type {TSESTreeNode} */ (variable.defs[0].node)
         if (defNode.type === 'TSInterfaceDeclaration') {
@@ -372,7 +376,10 @@ function inferRuntimeType(context, node, checked = new Set()) {
     }
     case 'TSTypeReference': {
       if (node.typeName.type === 'Identifier') {
-        const variable = findVariable(context.getScope(), node.typeName.name)
+        const variable = findVariable(
+          getScope(context, /** @type {any} */ (node)),
+          node.typeName.name
+        )
         if (variable && variable.defs.length === 1) {
           const defNode = /** @type {TSESTreeNode} */ (variable.defs[0].node)
           if (defNode.type === 'TSInterfaceDeclaration') {
diff --git a/lib/utils/ts-utils/ts-types.js b/lib/utils/ts-utils/ts-types.js
index e684831e..abb30386 100644
--- a/lib/utils/ts-utils/ts-types.js
+++ b/lib/utils/ts-utils/ts-types.js
@@ -45,14 +45,15 @@ module.exports = {
  * @returns {Services|null}
  */
 function getTSParserServices(context) {
-  const tsNodeMap = context.parserServices.esTreeNodeToTSNodeMap
+  const sourceCode = context.getSourceCode()
+  const tsNodeMap = sourceCode.parserServices.esTreeNodeToTSNodeMap
   if (!tsNodeMap) return null
   const hasFullTypeInformation =
-    context.parserServices.hasFullTypeInformation !== false
+    sourceCode.parserServices.hasFullTypeInformation !== false
   const checker =
     (hasFullTypeInformation &&
-      context.parserServices.program &&
-      context.parserServices.program.getTypeChecker()) ||
+      sourceCode.parserServices.program &&
+      sourceCode.parserServices.program.getTypeChecker()) ||
     null
   if (!checker) return null
   const ts = getTypeScript()


Test patch:
diff --git a/tests/lib/rules/no-deprecated-slot-attribute.js b/tests/lib/rules/no-deprecated-slot-attribute.js
index 8f61e922..7f3bcbf0 100644
--- a/tests/lib/rules/no-deprecated-slot-attribute.js
+++ b/tests/lib/rules/no-deprecated-slot-attribute.js
@@ -46,7 +46,19 @@ tester.run('no-deprecated-slot-attribute', rule, {
       <LinkList>
         <a />
       </LinkList>
-    </template>`
+    </template>`,
+    {
+      code: `<template>
+      <LinkList>
+        <one slot="one" />
+        <two slot="two" />
+        <my-component slot="my-component-slot" />
+        <myComponent slot="myComponent-slot" />
+        <MyComponent slot="MyComponent-slot" />
+      </LinkList>
+    </template>`,
+      options: [{ ignore: ['one', 'two', 'my-component'] }]
+    }
   ],
   invalid: [
     {
@@ -594,6 +606,26 @@ tester.run('no-deprecated-slot-attribute', rule, {
         '`slot` attributes are deprecated.',
         '`slot` attributes are deprecated.'
       ]
+    },
+    {
+      code: `
+      <template>
+        <my-component>
+          <one slot="one">
+            A
+          </one>
+          <two slot="two">
+            B
+          </two>
+        </my-component>
+      </template>`,
+      output: null,
+      options: [
+        {
+          ignore: ['one']
+        }
+      ],
+      errors: ['`slot` attributes are deprecated.']
     }
   ]
 })