function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
  const eslintSourceCode = context.getSourceCode()
  const rootNode = options.applyDocument
    ? eslintSourceCode.parserServices.getDocumentFragment &&
      eslintSourceCode.parserServices.getDocumentFragment()
    : eslintSourceCode.ast.templateBody
  /** @type {Token[] | null} */
  let tokensAndComments = null
  function getTokensAndComments() {
    if (tokensAndComments) {
      return tokensAndComments
    }
    tokensAndComments = rootNode
      ? tokenStore.getTokens(rootNode, {
          includeComments: true
        })
      : []
    return tokensAndComments
  }

  /** @param {number} index */
  function getNodeByRangeIndex(index) {
    if (!rootNode) {
      return eslintSourceCode.ast
    }

    /** @type {ASTNode} */
    let result = eslintSourceCode.ast
    /** @type {ASTNode[]} */
    const skipNodes = []
    let breakFlag = false

    traverseNodes(rootNode, {
      enterNode(node, parent) {
        if (breakFlag) {
          return
        }
        if (skipNodes[0] === parent) {
          skipNodes.unshift(node)
          return
        }
        if (node.range[0] <= index && index < node.range[1]) {
          result = node
        } else {
          skipNodes.unshift(node)
        }
      },
      leaveNode(node) {
        if (breakFlag) {
          return
        }
        if (result === node) {
          breakFlag = true
        } else if (skipNodes[0] === node) {
          skipNodes.shift()
        }
      }
    })
    return result
  }
  const sourceCode = newProxy(
    eslintSourceCode,
    {
      get tokensAndComments() {
        return getTokensAndComments()
      },
      getNodeByRangeIndex,
      // @ts-expect-error -- Added in ESLint v8.38.0
      getDeclaredVariables
    },
    tokenStore
  )

  /** @type {WeakMap<ASTNode, import('eslint').Scope.ScopeManager>} */
  const containerScopes = new WeakMap()

  /**
   * @param {ASTNode} node
   * @returns {import('eslint').Scope.ScopeManager|null}
   */
  function getContainerScope(node) {
    const exprContainer = getVExpressionContainer(node)
    if (!exprContainer) {
      return null
    }
    const cache = containerScopes.get(exprContainer)
    if (cache) {
      return cache
    }
    const programNode = eslintSourceCode.ast
    const parserOptions =
      context.languageOptions?.parserOptions ?? context.parserOptions ?? {}
    const ecmaFeatures = parserOptions.ecmaFeatures || {}
    const ecmaVersion =
      context.languageOptions?.ecmaVersion ?? parserOptions.ecmaVersion ?? 2020
    const sourceType = programNode.sourceType
    try {
      const eslintScope = createRequire(require.resolve('eslint'))(
        'eslint-scope'
      )
      const expStmt = newProxy(exprContainer, {
        // @ts-expect-error
        type: 'ExpressionStatement'
      })
      const scopeProgram = newProxy(programNode, {
        // @ts-expect-error
        body: [expStmt]
      })
      const scope = eslintScope.analyze(scopeProgram, {
        ignoreEval: true,
        nodejsScope: false,
        impliedStrict: ecmaFeatures.impliedStrict,
        ecmaVersion,
        sourceType,
        fallback: getFallbackKeys
      })
      containerScopes.set(exprContainer, scope)
      return scope
    } catch (error) {
      // ignore
      // console.log(error)
    }

    return null
  }
  return newProxy(context, {
    getSourceCode() {
      return sourceCode
    },
    get sourceCode() {
      return sourceCode
    },
    getDeclaredVariables
  })

  /**
   * @param {ESNode} node
   * @returns {Variable[]}
   */
  function getDeclaredVariables(node) {
    const scope = getContainerScope(node)
    return (
      scope?.getDeclaredVariables?.(node) ??
      context.getDeclaredVariables?.(node) ??
      []
    )
  }
}
Wrap the rule context object to override methods which access to tokens (such as getTokenAfter).
@param {RuleContext} context The rule context object.
@param {ParserServices.TokenStore} tokenStore The token store object for template.
@param {Object} options The option of this rule.
@param {boolean} [options.applyDocument] If `true`, apply check to document fragment.
@returns {RuleContext}